From 4b8d2bcc1ed9acd06d53c9018adb5826abad09a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2019 11:47:15 +0000 Subject: [PATCH 01/24] Bump lodash.mergewith from 4.6.1 to 4.6.2 Bumps [lodash.mergewith](https://github.com/lodash/lodash) from 4.6.1 to 4.6.2. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/commits) Signed-off-by: dependabot[bot] --- package-lock.json | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index fa717561..c7bcb1d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3223,7 +3223,8 @@ "version": "2.1.1", "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3247,13 +3248,15 @@ "version": "1.0.0", "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3270,19 +3273,22 @@ "version": "1.1.0", "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3413,7 +3419,8 @@ "version": "2.0.3", "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3427,6 +3434,7 @@ "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3443,6 +3451,7 @@ "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3451,13 +3460,15 @@ "version": "0.0.8", "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "resolved": false, "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -3478,6 +3489,7 @@ "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3566,7 +3578,8 @@ "version": "1.0.1", "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3580,6 +3593,7 @@ "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3675,7 +3689,8 @@ "version": "5.1.1", "resolved": false, "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3717,6 +3732,7 @@ "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3738,6 +3754,7 @@ "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3786,13 +3803,15 @@ "version": "1.0.2", "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "resolved": false, "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", - "dev": true + "dev": true, + "optional": true } } }, @@ -4832,9 +4851,9 @@ "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, "lodash.mergewith": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", - "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true }, "logform": { From ced3256165f239506a5295ec56b6a15fa5943f7e Mon Sep 17 00:00:00 2001 From: Bernard Date: Tue, 2 Jul 2019 11:48:21 -0700 Subject: [PATCH 02/24] Add leaderboard tables and models --- src/db/index.js | 6 ++++++ src/models/event-leaderboard.js | 26 ++++++++++++++++++++++++++ src/models/global-leaderboard.js | 24 ++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 src/models/event-leaderboard.js create mode 100644 src/models/global-leaderboard.js diff --git a/src/db/index.js b/src/db/index.js index b9debdad..84265bc8 100644 --- a/src/db/index.js +++ b/src/db/index.js @@ -13,6 +13,8 @@ const db = { Withdraws: undefined, Blocks: undefined, TransactionReceipts: undefined, + GlobalLeaderboard: undefined, + EventLeaderboard: undefined, }; /** @@ -28,6 +30,8 @@ const initDB = async () => { db.Withdraws = datastore({ filename: `${dbDir}/withdraws.db` }); db.Blocks = datastore({ filename: `${dbDir}/blocks.db` }); db.TransactionReceipts = datastore({ filename: `${dbDir}/transactionreceipts.db` }); + db.GlobalLeaderboard = datastore({ filename: `${dbDir}/globalleaderboard.db` }); + db.EventLeaderboard = datastore({ filename: `${dbDir}/eventleaderboard.db` }); try { await Promise.all([ @@ -37,6 +41,8 @@ const initDB = async () => { db.Withdraws.loadDatabase(), db.Blocks.loadDatabase(), db.TransactionReceipts.loadDatabase(), + db.GlobalLeaderboard.loadDatabase(), + db.EventLeaderboard.loadDatabase(), ]); await db.Blocks.ensureIndex({ fieldName: 'blockNum', unique: true }); diff --git a/src/models/event-leaderboard.js b/src/models/event-leaderboard.js new file mode 100644 index 00000000..60cf7baf --- /dev/null +++ b/src/models/event-leaderboard.js @@ -0,0 +1,26 @@ +/* eslint no-underscore-dangle: 0 */ +const { isFinite, isString } = require('lodash'); +const { toLowerCase } = require('../utils'); + +module.exports = class GlobalLeaderboard { + constructor(params) { + this.validate(params); + this.format(params); + } + + validate(params) { + if (!isString(params.userAddress)) throw Error('userAddress must be a String'); + if (!isString(params.eventAddress)) throw Error('eventAddress must be a String'); + if (!isString(params.investments)) throw Error('investments must be a Number'); + if (!isFinite(params.winnings)) throw Error('winnings must be a Number'); + if (!isString(params.returnRatio)) throw Error('returnRatio must be a Number'); + } + + format(params) { + this.eventAddress = toLowerCase(params.eventAddress); + this.userAddress = toLowerCase(params.userAddress); + this.investments = params.investments; + this.winnings = params.winnings; + this.returnRatio = params.returnRatio; + } +}; diff --git a/src/models/global-leaderboard.js b/src/models/global-leaderboard.js new file mode 100644 index 00000000..009241ef --- /dev/null +++ b/src/models/global-leaderboard.js @@ -0,0 +1,24 @@ +/* eslint no-underscore-dangle: 0 */ +const { isFinite, isString } = require('lodash'); +const { toLowerCase } = require('../utils'); + +module.exports = class GlobalLeaderboard { + constructor(params) { + this.validate(params); + this.format(params); + } + + validate(params) { + if (!isString(params.userAddress)) throw Error('userAddress must be a String'); + if (!isString(params.investments)) throw Error('investments must be a Number'); + if (!isFinite(params.winnings)) throw Error('winnings must be a Number'); + if (!isString(params.returnRatio)) throw Error('returnRatio must be a Number'); + } + + format(params) { + this.userAddress = toLowerCase(params.userAddress); + this.investments = params.investments; + this.winnings = params.winnings; + this.returnRatio = params.returnRatio; + } +}; From c34899b29e629b05c45937b9cda264ae2fd046db Mon Sep 17 00:00:00 2001 From: Bernard Date: Tue, 2 Jul 2019 15:24:07 -0700 Subject: [PATCH 03/24] Add helper methods for event leaderboard --- src/db/db-helper.js | 69 ++++++++++++++++++++++++++++++++++++++++++ src/sync/bet-placed.js | 1 + 2 files changed, 70 insertions(+) diff --git a/src/db/db-helper.js b/src/db/db-helper.js index a59cd327..829a70b8 100644 --- a/src/db/db-helper.js +++ b/src/db/db-helper.js @@ -440,4 +440,73 @@ module.exports = class DBHelper { throw err; } } + + /* EventLeaderboard */ + static async findEventLeaderboard(query, sort) { + try { + if (sort) return db.EventLeaderboard.cfind(query).sort(sort).exec(); + return db.EventLeaderboard.find(query); + } catch (err) { + logger.error(`FIND EventLeaderboard error: ${err.message}`); + throw err; + } + } + + static async findOneEventLeaderboard(query) { + try { + return db.EventLeaderboard.findOne(query); + } catch (err) { + logger.error(`FINDONE EventLeaderboard error: ${err.message}`); + throw err; + } + } + + static async countEventLeaderboard(query) { + try { + return db.EventLeaderboard.count(query); + } catch (err) { + logger.error(`COUNT EventLeaderboard error: ${err.message}`); + throw err; + } + } + + static async insertEventLeaderboard(entry) { + try { + const existing = await DBHelper.findOneEventLeaderboard({ + userAddress: entry.userAddress, + eventAddress: entry.eventAddress, + }); + if (isNull(existing)) { + await db.EventLeaderboard.insert(entry); + } else { + await DBHelper.updateEventLeaderboard(existing, entry); + } + } catch (err) { + logger.error(`INSERT EventLeaderboard error: ${err.message}`); + throw err; + } + } + + static async updateEventLeaderboard(existing, entry) { + const investments = existing.investments + entry.investments; + const winnings = existing.winnings + entry.winnings; + try { + await db.EventLeaderboard.update( + { + userAddress: entry.userAddress, + eventAddress: entry.eventAddress, + }, + { + $set: { + investments, + winnings, + returnRatio: winnings / investments, + }, + }, + ); + } catch (err) { + logger.error(`UPDATE EventLeaderboard error: ${err.message}`); + throw err; + } + } }; diff --git a/src/sync/bet-placed.js b/src/sync/bet-placed.js index d354c12d..07c48860 100644 --- a/src/sync/bet-placed.js +++ b/src/sync/bet-placed.js @@ -7,6 +7,7 @@ const { getTransactionReceipt } = require('../utils/web3-utils'); const DBHelper = require('../db/db-helper'); const EventSig = require('../config/event-sig'); const parseBet = require('./parsers/bet'); +const MultipleResultsEventApi = require('../api/multiple-results-event'); const syncBetPlaced = async ({ startBlock, endBlock, syncPromises, limit }) => { try { From c644568679ee538ed131eb6afa29d585e74d7e99 Mon Sep 17 00:00:00 2001 From: Bernard Date: Tue, 2 Jul 2019 15:33:19 -0700 Subject: [PATCH 04/24] Add helper methods for global leaderboard --- src/db/db-helper.js | 63 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/db/db-helper.js b/src/db/db-helper.js index 829a70b8..f1d64a53 100644 --- a/src/db/db-helper.js +++ b/src/db/db-helper.js @@ -509,4 +509,67 @@ module.exports = class DBHelper { throw err; } } + + /* GlobalLeaderboard */ + static async findGlobalLeaderboard(query, sort) { + try { + if (sort) return db.GlobalLeaderboard.cfind(query).sort(sort).exec(); + return db.GlobalLeaderboard.find(query); + } catch (err) { + logger.error(`FIND GlobalLeaderboard error: ${err.message}`); + throw err; + } + } + + static async findOneGlobalLeaderboard(query) { + try { + return db.GlobalLeaderboard.findOne(query); + } catch (err) { + logger.error(`FINDONE GlobalLeaderboard error: ${err.message}`); + throw err; + } + } + + static async countGlobalLeaderboard(query) { + try { + return db.GlobalLeaderboard.count(query); + } catch (err) { + logger.error(`COUNT GlobalLeaderboard error: ${err.message}`); + throw err; + } + } + + static async insertGlobalLeaderboard(entry) { + try { + const existing = await DBHelper.findOneGlobalLeaderboard({ userAddress: entry.userAddress }); + if (isNull(existing)) { + await db.GlobalLeaderboard.insert(entry); + } else { + await DBHelper.updateGlobalLeaderboard(existing, entry); + } + } catch (err) { + logger.error(`INSERT GlobalLeaderboard error: ${err.message}`); + throw err; + } + } + + static async updateGlobalLeaderboard(existing, entry) { + const investments = existing.investments + entry.investments; + const winnings = existing.winnings + entry.winnings; + try { + await db.GlobalLeaderboard.update( + { userAddress: entry.userAddress }, + { + $set: { + investments, + winnings, + returnRatio: winnings / investments, + }, + }, + ); + } catch (err) { + logger.error(`UPDATE GlobalLeaderboard error: ${err.message}`); + throw err; + } + } }; From afb8496577a0c0a1138da60c8444cec525a2e86a Mon Sep 17 00:00:00 2001 From: Bernard Date: Tue, 2 Jul 2019 15:39:57 -0700 Subject: [PATCH 05/24] Index both tables --- src/db/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/db/index.js b/src/db/index.js index 84265bc8..7bf65500 100644 --- a/src/db/index.js +++ b/src/db/index.js @@ -51,6 +51,8 @@ const initDB = async () => { await db.ResultSets.ensureIndex({ fieldName: 'txid', unique: true }); await db.Withdraws.ensureIndex({ fieldName: 'txid', unique: true }); await db.TransactionReceipts.ensureIndex({ fieldName: 'transactionHash' }); + await db.GlobalLeaderboard.ensureIndex({ fieldName: 'userAddress', unique: true }); + await db.EventLeaderboard.ensureIndex({ fieldName: 'eventAddress' }); if (process.env.TEST_ENV !== 'true') { await applyMigrations(); From 5def03e7bc3d3fa32021bce2860be81b9f12d4d1 Mon Sep 17 00:00:00 2001 From: Bernard Date: Tue, 2 Jul 2019 16:54:34 -0700 Subject: [PATCH 06/24] Update event leaderboard investment when bet, result set, vote --- src/db/db-helper.js | 13 +++++++++---- src/models/event-leaderboard.js | 3 +-- src/models/global-leaderboard.js | 3 +-- src/sync/bet-placed.js | 18 ++++++++++++++++++ src/sync/result-set.js | 19 +++++++++++++++++++ src/sync/vote-placed.js | 19 +++++++++++++++++++ 6 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/db/db-helper.js b/src/db/db-helper.js index f1d64a53..33c65284 100644 --- a/src/db/db-helper.js +++ b/src/db/db-helper.js @@ -3,6 +3,7 @@ const logger = require('../utils/logger'); const { isDefined } = require('../utils'); const { EVENT_STATUS, TX_STATUS } = require('../constants'); const { db } = require('.'); +const web3 = require('../web3'); module.exports = class DBHelper { /* Blocks */ @@ -488,8 +489,10 @@ module.exports = class DBHelper { } static async updateEventLeaderboard(existing, entry) { - const investments = existing.investments + entry.investments; - const winnings = existing.winnings + entry.winnings; + const existingInvest = web3.utils.toBN(existing.investments); + const investments = existingInvest.add(web3.utils.toBN(entry.investments)).toString(10); + const existingWin = web3.utils.toBN(existing.winnings); + const winnings = existingWin.add(web3.utils.toBN(entry.winnings)).toString(10); try { await db.EventLeaderboard.update( { @@ -554,8 +557,10 @@ module.exports = class DBHelper { } static async updateGlobalLeaderboard(existing, entry) { - const investments = existing.investments + entry.investments; - const winnings = existing.winnings + entry.winnings; + const existingInvest = web3.utils.toBN(existing.investments); + const investments = existingInvest.add(web3.utils.toBN(entry.investments)).toString(10); + const existingWin = web3.utils.toBN(existing.winnings); + const winnings = existingWin.add(web3.utils.toBN(entry.winnings)).toString(10); try { await db.GlobalLeaderboard.update( { userAddress: entry.userAddress }, diff --git a/src/models/event-leaderboard.js b/src/models/event-leaderboard.js index 60cf7baf..b715661c 100644 --- a/src/models/event-leaderboard.js +++ b/src/models/event-leaderboard.js @@ -13,7 +13,6 @@ module.exports = class GlobalLeaderboard { if (!isString(params.eventAddress)) throw Error('eventAddress must be a String'); if (!isString(params.investments)) throw Error('investments must be a Number'); if (!isFinite(params.winnings)) throw Error('winnings must be a Number'); - if (!isString(params.returnRatio)) throw Error('returnRatio must be a Number'); } format(params) { @@ -21,6 +20,6 @@ module.exports = class GlobalLeaderboard { this.userAddress = toLowerCase(params.userAddress); this.investments = params.investments; this.winnings = params.winnings; - this.returnRatio = params.returnRatio; + this.returnRatio = this.winnings / this.investments; } }; diff --git a/src/models/global-leaderboard.js b/src/models/global-leaderboard.js index 009241ef..a02b16c2 100644 --- a/src/models/global-leaderboard.js +++ b/src/models/global-leaderboard.js @@ -12,13 +12,12 @@ module.exports = class GlobalLeaderboard { if (!isString(params.userAddress)) throw Error('userAddress must be a String'); if (!isString(params.investments)) throw Error('investments must be a Number'); if (!isFinite(params.winnings)) throw Error('winnings must be a Number'); - if (!isString(params.returnRatio)) throw Error('returnRatio must be a Number'); } format(params) { this.userAddress = toLowerCase(params.userAddress); this.investments = params.investments; this.winnings = params.winnings; - this.returnRatio = params.returnRatio; + this.returnRatio = this.winnings / this.investments; } }; diff --git a/src/sync/bet-placed.js b/src/sync/bet-placed.js index 07c48860..d7e6840a 100644 --- a/src/sync/bet-placed.js +++ b/src/sync/bet-placed.js @@ -8,6 +8,7 @@ const DBHelper = require('../db/db-helper'); const EventSig = require('../config/event-sig'); const parseBet = require('./parsers/bet'); const MultipleResultsEventApi = require('../api/multiple-results-event'); +const EventLeaderboard = require('../models/event-leaderboard'); const syncBetPlaced = async ({ startBlock, endBlock, syncPromises, limit }) => { try { @@ -31,6 +32,15 @@ const syncBetPlaced = async ({ startBlock, endBlock, syncPromises, limit }) => { // Fetch and insert tx receipt const txReceipt = await getTransactionReceipt(bet.txid); await DBHelper.insertTransactionReceipt(txReceipt); + + // Update event leaderboard investments + const leaderboardEntry = new EventLeaderboard({ + eventAddress: bet.eventAddress, + userAddress: bet.betterAddress, + investments: bet.amount, + winnings: 0, + }); + await DBHelper.insertEventLeaderboard(leaderboardEntry); } catch (insertErr) { logger.error('Error syncBetPlaced parse'); throw insertErr; @@ -73,6 +83,14 @@ const pendingBetPlaced = async ({ syncPromises, limit }) => { if (foundLog) { const bet = parseBet({ log: foundLog }); await DBHelper.insertBet(bet); + // Update event leaderboard investments + const leaderboardEntry = new EventLeaderboard({ + eventAddress: bet.eventAddress, + userAddress: bet.betterAddress, + investments: bet.amount, + winnings: 0, + }); + await DBHelper.insertEventLeaderboard(leaderboardEntry); } } else { // Update bet with failed status diff --git a/src/sync/result-set.js b/src/sync/result-set.js index de9b3649..5a7a43ee 100644 --- a/src/sync/result-set.js +++ b/src/sync/result-set.js @@ -8,6 +8,7 @@ const { getTransactionReceipt } = require('../utils/web3-utils'); const DBHelper = require('../db/db-helper'); const EventSig = require('../config/event-sig'); const parseResultSet = require('./parsers/result-set'); +const EventLeaderboard = require('../models/event-leaderboard'); const syncResultSet = async ({ startBlock, endBlock, syncPromises, limit }) => { try { @@ -34,6 +35,15 @@ const syncResultSet = async ({ startBlock, endBlock, syncPromises, limit }) => { // Update event await updateEvent(resultSet); + + // Update event leaderboard investments + const leaderboardEntry = new EventLeaderboard({ + eventAddress: resultSet.eventAddress, + userAddress: resultSet.centralizedOracleAddress, + investments: resultSet.amount, + winnings: 0, + }); + await DBHelper.insertEventLeaderboard(leaderboardEntry); } catch (insertErr) { logger.error('Error syncResultSet parse'); throw insertErr; @@ -79,6 +89,15 @@ const pendingResultSet = async ({ syncPromises, limit }) => { // Update event await updateEvent(resultSet); + + // Update event leaderboard investments + const leaderboardEntry = new EventLeaderboard({ + eventAddress: resultSet.eventAddress, + userAddress: resultSet.centralizedOracleAddress, + investments: resultSet.amount, + winnings: 0, + }); + await DBHelper.insertEventLeaderboard(leaderboardEntry); } } else { // Update result set with failed status diff --git a/src/sync/vote-placed.js b/src/sync/vote-placed.js index 6ffa3d9b..c1b4d55a 100644 --- a/src/sync/vote-placed.js +++ b/src/sync/vote-placed.js @@ -7,6 +7,7 @@ const { getTransactionReceipt } = require('../utils/web3-utils'); const DBHelper = require('../db/db-helper'); const EventSig = require('../config/event-sig'); const parseBet = require('./parsers/bet'); +const EventLeaderboard = require('../models/event-leaderboard'); const syncVotePlaced = async ({ startBlock, endBlock, syncPromises, limit }) => { try { @@ -30,6 +31,15 @@ const syncVotePlaced = async ({ startBlock, endBlock, syncPromises, limit }) => // Fetch and insert tx receipt const txReceipt = await getTransactionReceipt(bet.txid); await DBHelper.insertTransactionReceipt(txReceipt); + + // Update event leaderboard investments + const leaderboardEntry = new EventLeaderboard({ + eventAddress: bet.eventAddress, + userAddress: bet.betterAddress, + investments: bet.amount, + winnings: 0, + }); + await DBHelper.insertEventLeaderboard(leaderboardEntry); } catch (insertErr) { logger.error('Error syncVotePlaced parse'); throw insertErr; @@ -72,6 +82,15 @@ const pendingVotePlaced = async ({ syncPromises, limit }) => { if (foundLog) { const bet = parseBet({ log: foundLog }); await DBHelper.insertBet(bet); + + // Update event leaderboard investments + const leaderboardEntry = new EventLeaderboard({ + eventAddress: bet.eventAddress, + userAddress: bet.betterAddress, + investments: bet.amount, + winnings: 0, + }); + await DBHelper.insertEventLeaderboard(leaderboardEntry); } } else { // Update bet with failed status From eb29f515d784759c4182cc5ffc49a1e1a85daa49 Mon Sep 17 00:00:00 2001 From: Bernard Date: Tue, 2 Jul 2019 18:00:53 -0700 Subject: [PATCH 07/24] Make leaderboards updated when event status changes to withdrawing --- src/db/db-helper.js | 79 ++++++++++++++++++++++++++++++++++++++++-- src/sync/bet-placed.js | 1 - 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/db/db-helper.js b/src/db/db-helper.js index 33c65284..fc2f9a4e 100644 --- a/src/db/db-helper.js +++ b/src/db/db-helper.js @@ -1,9 +1,12 @@ -const { isNull } = require('lodash'); +const { isNull, each, find } = require('lodash'); const logger = require('../utils/logger'); const { isDefined } = require('../utils'); const { EVENT_STATUS, TX_STATUS } = require('../constants'); const { db } = require('.'); const web3 = require('../web3'); +const MultipleResultsEventApi = require('../api/multiple-results-event'); +const EventLeaderboard = require('../models/event-leaderboard'); +const GlobalLeaderboard = require('../models/global-leaderboard'); module.exports = class DBHelper { /* Blocks */ @@ -242,7 +245,7 @@ module.exports = class DBHelper { static async updateEventStatusWithdrawing(currBlockTime) { try { - await db.Events.update( + const updatedEvents = await db.Events.update( { txStatus: TX_STATUS.SUCCESS, $not: { status: EVENT_STATUS.WITHDRAWING }, @@ -250,14 +253,84 @@ module.exports = class DBHelper { currentRound: { $gt: 0 }, }, { $set: { status: EVENT_STATUS.WITHDRAWING } }, - { multi: true }, + { + multi: true, + returnUpdatedDocs: true, + }, ); + await DBHelper.updateLeaderboardWithdrawing(updatedEvents); } catch (err) { logger.error(`UPDATE Event Status Withdrawing error: ${err.message}`); throw err; } } + static async updateLeaderboardWithdrawing(events) { + try { + each(events, async (event) => { + // get all the participants + const bets = await DBHelper.findBet({ eventAddress: event.address }); + const filtered = []; + each(bets, (bet) => { + if (!find(filtered, { + eventAddress: bet.eventAddress, + betterAddress: bet.betterAddress, + })) { + filtered.push(bet); + } + }); + // calculate winnings for all participants + const promises = []; + const winnings = []; + each(filtered, (bet) => { + promises.push(new Promise(async (resolve, reject) => { + try { + const { eventAddress, betterAddress } = bet; + const res = await MultipleResultsEventApi.calculateWinnings({ + eventAddress, + address: betterAddress, + }); + winnings.push({ + eventAddress, + betterAddress, + amount: res.toString(10), + }); + resolve(); + } catch (err) { + reject(err); + } + })); + }); + await Promise.all(promises); + // update leaderboard + each(winnings, async (winning) => { + // update event leaderboard winnings + const eventLeaderboardEntry = new EventLeaderboard({ + eventAddress: winning.eventAddress, + userAddress: winning.betterAddress, + investments: 0, + winnings: winning.amount, + }); + await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); + // update global leaderboard investments, winnings + const userAmountInEvent = await DBHelper.findOneEventLeaderboard({ + eventAddress: winning.eventAddress, + userAddress: winning.betterAddress, + }); + const globalLeaderboard = new GlobalLeaderboard({ + userAddress: winning.betterAddress, + investments: userAmountInEvent.investments, + winnings: winning.amount, + }); + await DBHelper.insertGlobalLeaderboard(globalLeaderboard); + }); + }); + } catch (err) { + logger.error(`UPDATE leaderboard when withdrawing status changed error: ${err.message}`); + throw err; + } + } + static async updateEventWithdrawnList(withdraw) { try { const event = await DBHelper.findOneEvent({ address: withdraw.eventAddress }); diff --git a/src/sync/bet-placed.js b/src/sync/bet-placed.js index d7e6840a..3c27174a 100644 --- a/src/sync/bet-placed.js +++ b/src/sync/bet-placed.js @@ -7,7 +7,6 @@ const { getTransactionReceipt } = require('../utils/web3-utils'); const DBHelper = require('../db/db-helper'); const EventSig = require('../config/event-sig'); const parseBet = require('./parsers/bet'); -const MultipleResultsEventApi = require('../api/multiple-results-event'); const EventLeaderboard = require('../models/event-leaderboard'); const syncBetPlaced = async ({ startBlock, endBlock, syncPromises, limit }) => { From 24efc4efd84ae974eef8614b7097c0184b300346 Mon Sep 17 00:00:00 2001 From: Bernard Date: Wed, 3 Jul 2019 14:27:40 -0700 Subject: [PATCH 08/24] Changes requested --- package-lock.json | 5 ++++ package.json | 1 + src/db/db-helper.js | 48 +++++++++++++++----------------- src/models/event-leaderboard.js | 13 +++++---- src/models/global-leaderboard.js | 11 ++++---- src/sync/bet-placed.js | 4 +-- src/sync/result-set.js | 4 +-- src/sync/vote-placed.js | 4 +-- 8 files changed, 47 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index fa717561..442898a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -991,6 +991,11 @@ "tweetnacl": "^0.14.3" } }, + "bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" + }, "binary-extensions": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", diff --git a/package.json b/package.json index b72124ac..fe8d4b36 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "async": "^3.0.1", "axios": "^0.18.0", "babel-polyfill": "^6.26.0", + "bignumber.js": "^9.0.0", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "crypto-random-string": "^3.0.1", diff --git a/src/db/db-helper.js b/src/db/db-helper.js index fc2f9a4e..a3b5c8a3 100644 --- a/src/db/db-helper.js +++ b/src/db/db-helper.js @@ -1,6 +1,7 @@ const { isNull, each, find } = require('lodash'); const logger = require('../utils/logger'); const { isDefined } = require('../utils'); +const { sumBN } = require('../utils/web3-utils'); const { EVENT_STATUS, TX_STATUS } = require('../constants'); const { db } = require('.'); const web3 = require('../web3'); @@ -553,7 +554,15 @@ module.exports = class DBHelper { if (isNull(existing)) { await db.EventLeaderboard.insert(entry); } else { - await DBHelper.updateEventLeaderboard(existing, entry); + const investments = sumBN(existing.investments, entry.investments).toString(10); + const winnings = sumBN(existing.winnings, entry.winnings).toString(10); + const newEntry = new EventLeaderboard({ + userAddress: entry.userAddress, + eventAddress: entry.eventAddress, + investments, + winnings, + }); // make a new instance, ratio will be calculated in the model + await DBHelper.updateEventLeaderboard(newEntry); } } catch (err) { logger.error(`INSERT EventLeaderboard error: ${err.message}`); @@ -561,24 +570,14 @@ module.exports = class DBHelper { } } - static async updateEventLeaderboard(existing, entry) { - const existingInvest = web3.utils.toBN(existing.investments); - const investments = existingInvest.add(web3.utils.toBN(entry.investments)).toString(10); - const existingWin = web3.utils.toBN(existing.winnings); - const winnings = existingWin.add(web3.utils.toBN(entry.winnings)).toString(10); + static async updateEventLeaderboard(entry) { try { await db.EventLeaderboard.update( { userAddress: entry.userAddress, eventAddress: entry.eventAddress, }, - { - $set: { - investments, - winnings, - returnRatio: winnings / investments, - }, - }, + { $set: entry }, ); } catch (err) { logger.error(`UPDATE EventLeaderboard error: ${err.message}`); @@ -621,7 +620,14 @@ module.exports = class DBHelper { if (isNull(existing)) { await db.GlobalLeaderboard.insert(entry); } else { - await DBHelper.updateGlobalLeaderboard(existing, entry); + const investments = sumBN(existing.investments, entry.investments).toString(10); + const winnings = sumBN(existing.winnings, entry.winnings).toString(10); + const newEntry = new GlobalLeaderboard({ + userAddress: entry.userAddress, + investments, + winnings, + }); // make a new instance, ratio will be calculated in the model + await DBHelper.updateGlobalLeaderboard(newEntry); } } catch (err) { logger.error(`INSERT GlobalLeaderboard error: ${err.message}`); @@ -629,21 +635,11 @@ module.exports = class DBHelper { } } - static async updateGlobalLeaderboard(existing, entry) { - const existingInvest = web3.utils.toBN(existing.investments); - const investments = existingInvest.add(web3.utils.toBN(entry.investments)).toString(10); - const existingWin = web3.utils.toBN(existing.winnings); - const winnings = existingWin.add(web3.utils.toBN(entry.winnings)).toString(10); + static async updateGlobalLeaderboard(entry) { try { await db.GlobalLeaderboard.update( { userAddress: entry.userAddress }, - { - $set: { - investments, - winnings, - returnRatio: winnings / investments, - }, - }, + { $set: entry }, ); } catch (err) { logger.error(`UPDATE GlobalLeaderboard error: ${err.message}`); diff --git a/src/models/event-leaderboard.js b/src/models/event-leaderboard.js index b715661c..43c66e60 100644 --- a/src/models/event-leaderboard.js +++ b/src/models/event-leaderboard.js @@ -1,8 +1,8 @@ -/* eslint no-underscore-dangle: 0 */ -const { isFinite, isString } = require('lodash'); +const { isString } = require('lodash'); +const BigNumber = require('bignumber.js'); const { toLowerCase } = require('../utils'); -module.exports = class GlobalLeaderboard { +module.exports = class EventLeaderboard { constructor(params) { this.validate(params); this.format(params); @@ -11,8 +11,8 @@ module.exports = class GlobalLeaderboard { validate(params) { if (!isString(params.userAddress)) throw Error('userAddress must be a String'); if (!isString(params.eventAddress)) throw Error('eventAddress must be a String'); - if (!isString(params.investments)) throw Error('investments must be a Number'); - if (!isFinite(params.winnings)) throw Error('winnings must be a Number'); + if (!isString(params.investments)) throw Error('investments must be a String'); + if (!isString(params.winnings)) throw Error('winnings must be a String'); } format(params) { @@ -20,6 +20,7 @@ module.exports = class GlobalLeaderboard { this.userAddress = toLowerCase(params.userAddress); this.investments = params.investments; this.winnings = params.winnings; - this.returnRatio = this.winnings / this.investments; + const ratio = new BigNumber(this.winnings).dividedBy(new BigNumber(this.investments)); + this.returnRatio = ratio.toString(); } }; diff --git a/src/models/global-leaderboard.js b/src/models/global-leaderboard.js index a02b16c2..5f26aa5c 100644 --- a/src/models/global-leaderboard.js +++ b/src/models/global-leaderboard.js @@ -1,5 +1,5 @@ -/* eslint no-underscore-dangle: 0 */ -const { isFinite, isString } = require('lodash'); +const { isString } = require('lodash'); +const BigNumber = require('bignumber.js'); const { toLowerCase } = require('../utils'); module.exports = class GlobalLeaderboard { @@ -10,14 +10,15 @@ module.exports = class GlobalLeaderboard { validate(params) { if (!isString(params.userAddress)) throw Error('userAddress must be a String'); - if (!isString(params.investments)) throw Error('investments must be a Number'); - if (!isFinite(params.winnings)) throw Error('winnings must be a Number'); + if (!isString(params.investments)) throw Error('investments must be a String'); + if (!isString(params.winnings)) throw Error('winnings must be a String'); } format(params) { this.userAddress = toLowerCase(params.userAddress); this.investments = params.investments; this.winnings = params.winnings; - this.returnRatio = this.winnings / this.investments; + const ratio = new BigNumber(this.winnings).dividedBy(new BigNumber(this.investments)); + this.returnRatio = ratio.toString(); } }; diff --git a/src/sync/bet-placed.js b/src/sync/bet-placed.js index 3c27174a..8a7a93ef 100644 --- a/src/sync/bet-placed.js +++ b/src/sync/bet-placed.js @@ -37,7 +37,7 @@ const syncBetPlaced = async ({ startBlock, endBlock, syncPromises, limit }) => { eventAddress: bet.eventAddress, userAddress: bet.betterAddress, investments: bet.amount, - winnings: 0, + winnings: '0', }); await DBHelper.insertEventLeaderboard(leaderboardEntry); } catch (insertErr) { @@ -87,7 +87,7 @@ const pendingBetPlaced = async ({ syncPromises, limit }) => { eventAddress: bet.eventAddress, userAddress: bet.betterAddress, investments: bet.amount, - winnings: 0, + winnings: '0', }); await DBHelper.insertEventLeaderboard(leaderboardEntry); } diff --git a/src/sync/result-set.js b/src/sync/result-set.js index 5a7a43ee..3d65e11a 100644 --- a/src/sync/result-set.js +++ b/src/sync/result-set.js @@ -41,7 +41,7 @@ const syncResultSet = async ({ startBlock, endBlock, syncPromises, limit }) => { eventAddress: resultSet.eventAddress, userAddress: resultSet.centralizedOracleAddress, investments: resultSet.amount, - winnings: 0, + winnings: '0', }); await DBHelper.insertEventLeaderboard(leaderboardEntry); } catch (insertErr) { @@ -95,7 +95,7 @@ const pendingResultSet = async ({ syncPromises, limit }) => { eventAddress: resultSet.eventAddress, userAddress: resultSet.centralizedOracleAddress, investments: resultSet.amount, - winnings: 0, + winnings: '0', }); await DBHelper.insertEventLeaderboard(leaderboardEntry); } diff --git a/src/sync/vote-placed.js b/src/sync/vote-placed.js index c1b4d55a..d14b29be 100644 --- a/src/sync/vote-placed.js +++ b/src/sync/vote-placed.js @@ -37,7 +37,7 @@ const syncVotePlaced = async ({ startBlock, endBlock, syncPromises, limit }) => eventAddress: bet.eventAddress, userAddress: bet.betterAddress, investments: bet.amount, - winnings: 0, + winnings: '0', }); await DBHelper.insertEventLeaderboard(leaderboardEntry); } catch (insertErr) { @@ -88,7 +88,7 @@ const pendingVotePlaced = async ({ syncPromises, limit }) => { eventAddress: bet.eventAddress, userAddress: bet.betterAddress, investments: bet.amount, - winnings: 0, + winnings: '0', }); await DBHelper.insertEventLeaderboard(leaderboardEntry); } From 579650d1a739d6f25c5ceea6badd59e5a40e8bcd Mon Sep 17 00:00:00 2001 From: Bernard Date: Wed, 3 Jul 2019 18:04:54 -0700 Subject: [PATCH 09/24] Leaderboard update on withdrawing changed --- src/db/db-helper.js | 69 +----------- src/sync/event-status-withdrawing-changed.js | 112 +++++++++++++++++++ src/sync/index.js | 6 +- 3 files changed, 118 insertions(+), 69 deletions(-) create mode 100644 src/sync/event-status-withdrawing-changed.js diff --git a/src/db/db-helper.js b/src/db/db-helper.js index a3b5c8a3..6bbdf105 100644 --- a/src/db/db-helper.js +++ b/src/db/db-helper.js @@ -5,7 +5,6 @@ const { sumBN } = require('../utils/web3-utils'); const { EVENT_STATUS, TX_STATUS } = require('../constants'); const { db } = require('.'); const web3 = require('../web3'); -const MultipleResultsEventApi = require('../api/multiple-results-event'); const EventLeaderboard = require('../models/event-leaderboard'); const GlobalLeaderboard = require('../models/global-leaderboard'); @@ -259,79 +258,13 @@ module.exports = class DBHelper { returnUpdatedDocs: true, }, ); - await DBHelper.updateLeaderboardWithdrawing(updatedEvents); + return updatedEvents; } catch (err) { logger.error(`UPDATE Event Status Withdrawing error: ${err.message}`); throw err; } } - static async updateLeaderboardWithdrawing(events) { - try { - each(events, async (event) => { - // get all the participants - const bets = await DBHelper.findBet({ eventAddress: event.address }); - const filtered = []; - each(bets, (bet) => { - if (!find(filtered, { - eventAddress: bet.eventAddress, - betterAddress: bet.betterAddress, - })) { - filtered.push(bet); - } - }); - // calculate winnings for all participants - const promises = []; - const winnings = []; - each(filtered, (bet) => { - promises.push(new Promise(async (resolve, reject) => { - try { - const { eventAddress, betterAddress } = bet; - const res = await MultipleResultsEventApi.calculateWinnings({ - eventAddress, - address: betterAddress, - }); - winnings.push({ - eventAddress, - betterAddress, - amount: res.toString(10), - }); - resolve(); - } catch (err) { - reject(err); - } - })); - }); - await Promise.all(promises); - // update leaderboard - each(winnings, async (winning) => { - // update event leaderboard winnings - const eventLeaderboardEntry = new EventLeaderboard({ - eventAddress: winning.eventAddress, - userAddress: winning.betterAddress, - investments: 0, - winnings: winning.amount, - }); - await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); - // update global leaderboard investments, winnings - const userAmountInEvent = await DBHelper.findOneEventLeaderboard({ - eventAddress: winning.eventAddress, - userAddress: winning.betterAddress, - }); - const globalLeaderboard = new GlobalLeaderboard({ - userAddress: winning.betterAddress, - investments: userAmountInEvent.investments, - winnings: winning.amount, - }); - await DBHelper.insertGlobalLeaderboard(globalLeaderboard); - }); - }); - } catch (err) { - logger.error(`UPDATE leaderboard when withdrawing status changed error: ${err.message}`); - throw err; - } - } - static async updateEventWithdrawnList(withdraw) { try { const event = await DBHelper.findOneEvent({ address: withdraw.eventAddress }); diff --git a/src/sync/event-status-withdrawing-changed.js b/src/sync/event-status-withdrawing-changed.js new file mode 100644 index 00000000..2d58ad80 --- /dev/null +++ b/src/sync/event-status-withdrawing-changed.js @@ -0,0 +1,112 @@ +const { each, isNull, find } = require('lodash'); +const web3 = require('../web3'); +const { TX_STATUS } = require('../constants'); +const EventSig = require('../config/event-sig'); +const { toLowerCase } = require('../utils'); +const { getTransactionReceipt } = require('../utils/web3-utils'); +const logger = require('../utils/logger'); +const DBHelper = require('../db/db-helper'); +const parseEvent = require('./parsers/multiple-results-event'); +const MultipleResultsEventApi = require('../api/multiple-results-event'); +const EventLeaderboard = require('../models/event-leaderboard'); +const GlobalLeaderboard = require('../models/global-leaderboard'); + +const getInvestments = async (txs) => { + const accumulated = txs.reduce((acc, cur) => { + const amount = web3.utils.toBN(cur.amount); + if (!cur.betterAddress) cur.betterAddress = cur.centralizedOracleAddress; + if (Object.keys(acc).includes(cur.betterAddress)) { + acc[cur.betterAddress] = web3.utils.toBN(acc[cur.betterAddress]).add(amount).toString(10); + } else { + acc[cur.betterAddress] = amount.toString(10); + } + return acc; + }, {}); + + return accumulated; +}; + +const updateLeaderboardWithdrawing = async (syncPromises, events, limit) => { + each(events, async (event) => { + syncPromises.push(limit(async () => { + try { + // get all the participants + const bets = await DBHelper.findBet({ + eventAddress: event.address, + txStatus: TX_STATUS.SUCCESS, + }); + const resultSets = await DBHelper.findResultSet({ + eventAddress: event.address, + txStatus: TX_STATUS.SUCCESS, + eventRound: 0, + }); + const txs = resultSets.concat(bets); + const investments = await getInvestments(txs); + const filtered = []; + each(txs, (tx) => { + if (!find(filtered, { + eventAddress: tx.eventAddress, + betterAddress: tx.betterAddress, + })) { + filtered.push(tx); + } + }); + // calculate winnings for all participants + const promises = []; + each(filtered, (tx) => { + promises.push(new Promise(async (resolve, reject) => { + try { + const { eventAddress, betterAddress, resultIndex, centralizedOracleAddress } = tx; + const userAddress = betterAddress || centralizedOracleAddress; + // calculate winning for this user in this event + let winning = '0'; + if (resultIndex === event.currentResultIndex) { + const res = await MultipleResultsEventApi.calculateWinnings({ + eventAddress, + address: userAddress, + }); + winning = res.toString(10); + } + // update event leaderboard winnings + const eventLeaderboardEntry = new EventLeaderboard({ + eventAddress, + userAddress, + investments: '0', // event leaderboard entry already has user's investments + winnings: winning, + }); + await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); + // update global leaderboard investments, winnings + const globalLeaderboard = new GlobalLeaderboard({ + userAddress, + investments: investments[userAddress], + winnings: winning, + }); + await DBHelper.insertGlobalLeaderboard(globalLeaderboard); + resolve(); + } catch (err) { + reject(err); + } + })); + }); + await Promise.all(promises); + } catch (err) { + logger.error(`UPDATE leaderboard when withdrawing status changed error: ${err.message}`); + throw err; + } + })); + }); +}; + +const updateEventStatusWithdrawingAndLeaderboard = async ( + { blockTime, syncPromises, limit }, +) => { + try { + const updatedEvents = await DBHelper.updateEventStatusWithdrawing(blockTime); + await updateLeaderboardWithdrawing(syncPromises, updatedEvents[1], limit); + } catch (err) { + logger.error('Error event status withdrawing changed'); + throw err; + } +}; + +module.exports = updateEventStatusWithdrawingAndLeaderboard; diff --git a/src/sync/index.js b/src/sync/index.js index 28500f84..abc18ac3 100644 --- a/src/sync/index.js +++ b/src/sync/index.js @@ -18,6 +18,7 @@ const { pendingWinningsWithdrawn, } = require('./winnings-withdrawn'); const syncBlocks = require('./blocks'); +const updateEventStatusWithdrawingAndLeaderboard = require('./event-status-withdrawing-changed'); const DBHelper = require('../db/db-helper'); const logger = require('../utils/logger'); const { publishSyncInfo } = require('../graphql/subscriptions'); @@ -205,7 +206,10 @@ const startSync = async () => { await DBHelper.updateEventStatusOracleResultSetting(blockTime); await DBHelper.updateEventStatusOpenResultSetting(blockTime); await DBHelper.updateEventStatusArbitration(blockTime); - await DBHelper.updateEventStatusWithdrawing(blockTime); + // await DBHelper.updateEventStatusWithdrawing(blockTime); + syncPromises = []; + await updateEventStatusWithdrawingAndLeaderboard({ blockTime, syncPromises, limit }); + await Promise.all(syncPromises); // Send syncInfo subscription message await publishSyncInfo(endBlock, blockTime); From 95a3158197bd7990e330350e31dc16cdcdb2d969 Mon Sep 17 00:00:00 2001 From: Bernard Date: Fri, 5 Jul 2019 11:47:09 -0700 Subject: [PATCH 10/24] Changes requested --- src/sync/bet-placed.js | 1 + src/sync/index.js | 6 ++--- ...awing-changed.js => update-leaderboard.js} | 27 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) rename src/sync/{event-status-withdrawing-changed.js => update-leaderboard.js} (84%) diff --git a/src/sync/bet-placed.js b/src/sync/bet-placed.js index 8a7a93ef..ad0ab79a 100644 --- a/src/sync/bet-placed.js +++ b/src/sync/bet-placed.js @@ -82,6 +82,7 @@ const pendingBetPlaced = async ({ syncPromises, limit }) => { if (foundLog) { const bet = parseBet({ log: foundLog }); await DBHelper.insertBet(bet); + // Update event leaderboard investments const leaderboardEntry = new EventLeaderboard({ eventAddress: bet.eventAddress, diff --git a/src/sync/index.js b/src/sync/index.js index abc18ac3..8b7c2091 100644 --- a/src/sync/index.js +++ b/src/sync/index.js @@ -18,7 +18,7 @@ const { pendingWinningsWithdrawn, } = require('./winnings-withdrawn'); const syncBlocks = require('./blocks'); -const updateEventStatusWithdrawingAndLeaderboard = require('./event-status-withdrawing-changed'); +const updateLeaderboard = require('./update-leaderboard'); const DBHelper = require('../db/db-helper'); const logger = require('../utils/logger'); const { publishSyncInfo } = require('../graphql/subscriptions'); @@ -206,9 +206,9 @@ const startSync = async () => { await DBHelper.updateEventStatusOracleResultSetting(blockTime); await DBHelper.updateEventStatusOpenResultSetting(blockTime); await DBHelper.updateEventStatusArbitration(blockTime); - // await DBHelper.updateEventStatusWithdrawing(blockTime); + const newWithdrawEvents = await DBHelper.updateEventStatusWithdrawing(blockTime); syncPromises = []; - await updateEventStatusWithdrawingAndLeaderboard({ blockTime, syncPromises, limit }); + await updateLeaderboard({ newWithdrawEvents, syncPromises, limit }); await Promise.all(syncPromises); // Send syncInfo subscription message diff --git a/src/sync/event-status-withdrawing-changed.js b/src/sync/update-leaderboard.js similarity index 84% rename from src/sync/event-status-withdrawing-changed.js rename to src/sync/update-leaderboard.js index 2d58ad80..767b50dc 100644 --- a/src/sync/event-status-withdrawing-changed.js +++ b/src/sync/update-leaderboard.js @@ -28,15 +28,15 @@ const getInvestments = async (txs) => { const updateLeaderboardWithdrawing = async (syncPromises, events, limit) => { each(events, async (event) => { - syncPromises.push(limit(async () => { + syncPromises.push(limit(async (eventObj) => { try { // get all the participants const bets = await DBHelper.findBet({ - eventAddress: event.address, + eventAddress: eventObj.address, txStatus: TX_STATUS.SUCCESS, }); const resultSets = await DBHelper.findResultSet({ - eventAddress: event.address, + eventAddress: eventObj.address, txStatus: TX_STATUS.SUCCESS, eventRound: 0, }); @@ -59,27 +59,27 @@ const updateLeaderboardWithdrawing = async (syncPromises, events, limit) => { const { eventAddress, betterAddress, resultIndex, centralizedOracleAddress } = tx; const userAddress = betterAddress || centralizedOracleAddress; // calculate winning for this user in this event - let winning = '0'; - if (resultIndex === event.currentResultIndex) { + let winnings = '0'; + if (resultIndex === eventObj.currentResultIndex) { const res = await MultipleResultsEventApi.calculateWinnings({ eventAddress, address: userAddress, }); - winning = res.toString(10); + winnings = res.toString(10); } // update event leaderboard winnings const eventLeaderboardEntry = new EventLeaderboard({ eventAddress, userAddress, investments: '0', // event leaderboard entry already has user's investments - winnings: winning, + winnings, }); await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); // update global leaderboard investments, winnings const globalLeaderboard = new GlobalLeaderboard({ userAddress, investments: investments[userAddress], - winnings: winning, + winnings, }); await DBHelper.insertGlobalLeaderboard(globalLeaderboard); resolve(); @@ -93,20 +93,19 @@ const updateLeaderboardWithdrawing = async (syncPromises, events, limit) => { logger.error(`UPDATE leaderboard when withdrawing status changed error: ${err.message}`); throw err; } - })); + }, event)); }); }; -const updateEventStatusWithdrawingAndLeaderboard = async ( - { blockTime, syncPromises, limit }, +const updateLeaderboard = async ( + { events, syncPromises, limit }, ) => { try { - const updatedEvents = await DBHelper.updateEventStatusWithdrawing(blockTime); - await updateLeaderboardWithdrawing(syncPromises, updatedEvents[1], limit); + await updateLeaderboardWithdrawing(syncPromises, events[1], limit); } catch (err) { logger.error('Error event status withdrawing changed'); throw err; } }; -module.exports = updateEventStatusWithdrawingAndLeaderboard; +module.exports = updateLeaderboard; From 714e2f3aca63a7cddcc1041809bd5295e47c1aeb Mon Sep 17 00:00:00 2001 From: Bernard Date: Fri, 5 Jul 2019 15:19:43 -0700 Subject: [PATCH 11/24] Add queries for leaderboard --- src/graphql/queries/index.js | 3 ++ src/graphql/queries/leaderboard.js | 53 ++++++++++++++++++++++++++++++ src/graphql/schema.js | 33 +++++++++++++++++++ src/sync/update-leaderboard.js | 4 +-- 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 src/graphql/queries/leaderboard.js diff --git a/src/graphql/queries/index.js b/src/graphql/queries/index.js index 8fd06452..502d19b4 100644 --- a/src/graphql/queries/index.js +++ b/src/graphql/queries/index.js @@ -10,6 +10,7 @@ const allStats = require('./all-stats'); const mostBets = require('./most-bets'); const biggestWinners = require('./biggest-winners'); const withdrawableEvents = require('./withdrawable-events'); +const { eventLeaderboard, globalLeaderboard } = require('./leaderboard'); module.exports = { events, @@ -24,4 +25,6 @@ module.exports = { mostBets, biggestWinners, withdrawableEvents, + eventLeaderboard, + globalLeaderboard, }; diff --git a/src/graphql/queries/leaderboard.js b/src/graphql/queries/leaderboard.js new file mode 100644 index 00000000..3a0bf8da --- /dev/null +++ b/src/graphql/queries/leaderboard.js @@ -0,0 +1,53 @@ +const { isArray, each } = require('lodash'); +const { lowercaseFilters, runPaginatedQuery } = require('./utils'); + +const buildFilters = ({ + userAddress, + eventAddress, +} = {}) => { + if (!eventAddress && !userAddress) { + throw Error('eventAddress or userAddress missing in filters'); + } + const filters = []; + const filter = {}; + if (eventAddress) filter.eventAddress = eventAddress; + if (userAddress) filter.betterAddress = userAddress; + + filters.push(filter); + return filters; +}; + +const eventLeaderboard = async ( + root, + { filter, orderBy, limit, skip }, + { db: { EventLeaderboard } }, +) => { + const query = filter ? { $or: buildFilters(lowercaseFilters(filter)) } : {}; + return runPaginatedQuery({ + db: EventLeaderboard, + filter: query, + orderBy, + limit, + skip, + }); +}; + +const globalLeaderboard = async ( + root, + { filter, orderBy, limit, skip }, + { db: { GlobalLeaderboard } }, +) => { + const query = filter ? { $or: buildFilters(lowercaseFilters(filter)) } : {}; + return runPaginatedQuery({ + db: GlobalLeaderboard, + filter: query, + orderBy, + limit, + skip, + }); +}; + +module.exports = { + eventLeaderboard, + globalLeaderboard, +}; diff --git a/src/graphql/schema.js b/src/graphql/schema.js index 1b743d66..284724da 100644 --- a/src/graphql/schema.js +++ b/src/graphql/schema.js @@ -239,11 +239,30 @@ type PaginatedBiggestWinner { items: [BiggestWinner]! } +type Leaderboard { + eventAddress: String + userAddress: String! + investments: String! + winnings: String! + returnRatio: String! +} + +type PaginatedLeaderboard { + totalCount: Int! + pageInfo: PageInfo + items: [Leaderboard]! +} + input Order { field: String! direction: OrderDirection! } +input LeaderboardFilter { + userAddress: String + eventAddress: String +} + input EventFilter { OR: [EventFilter!] txid: String @@ -398,6 +417,20 @@ type Query { limit: Int skip: Int ): PaginatedBiggestWinner! + + eventLeaderboard( + filter: LeaderboardFilter + orderBy: [Order!] + limit: Int + skip: Int + ): PaginatedLeaderboard! + + globalLeaderboard( + filter: LeaderboardFilter + orderBy: [Order!] + limit: Int + skip: Int + ): PaginatedLeaderboard! } type Mutation { diff --git a/src/sync/update-leaderboard.js b/src/sync/update-leaderboard.js index 767b50dc..85e7e0dd 100644 --- a/src/sync/update-leaderboard.js +++ b/src/sync/update-leaderboard.js @@ -98,10 +98,10 @@ const updateLeaderboardWithdrawing = async (syncPromises, events, limit) => { }; const updateLeaderboard = async ( - { events, syncPromises, limit }, + { newWithdrawEvents, syncPromises, limit }, ) => { try { - await updateLeaderboardWithdrawing(syncPromises, events[1], limit); + await updateLeaderboardWithdrawing(syncPromises, newWithdrawEvents[1], limit); } catch (err) { logger.error('Error event status withdrawing changed'); throw err; From 6a8cfb11e968e7858d34707d1df88ed8e55e1586 Mon Sep 17 00:00:00 2001 From: Bernard Date: Fri, 5 Jul 2019 17:18:28 -0700 Subject: [PATCH 12/24] Add tests --- src/graphql/queries/leaderboard.js | 2 +- test/db/index.js | 378 +++++++++++++++++++++++++++++ test/graphql/queries.tests.js | 77 ++++++ test/graphql/schema-helper.js | 17 ++ 4 files changed, 473 insertions(+), 1 deletion(-) diff --git a/src/graphql/queries/leaderboard.js b/src/graphql/queries/leaderboard.js index 3a0bf8da..eb329c3f 100644 --- a/src/graphql/queries/leaderboard.js +++ b/src/graphql/queries/leaderboard.js @@ -11,7 +11,7 @@ const buildFilters = ({ const filters = []; const filter = {}; if (eventAddress) filter.eventAddress = eventAddress; - if (userAddress) filter.betterAddress = userAddress; + if (userAddress) filter.userAddress = userAddress; filters.push(filter); return filters; diff --git a/test/db/index.js b/test/db/index.js index 494e7203..cac9f97d 100644 --- a/test/db/index.js +++ b/test/db/index.js @@ -1771,6 +1771,384 @@ describe('db', () => { }); }); + /* EventLeaderboard */ + describe('test EventLeaderboard db', () => { + const mockEventLeaderboard = [ + { + eventAddress: '0x09645ea6e4e1f5375f7596b73b3b597e6507201a', + userAddress: '0x939592864c0bd3355b2d54e4fa2203e8343b6d6a', + investments: '1000000000', + winnings: '1000000000', + returnRatio: '1', + }, + { + eventAddress: '0x09645ea6e4e1f5375f7596b73b3b597e6507201a', + userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', + investments: '5000000', + winnings: '5000000', + returnRatio: '1', + }, + { + eventAddress: '0x12345ea6e4e1f5375f7596b73b3b597e6507201a', + userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', + investments: '2000000', + winnings: '0', + returnRatio: '0', + }, + ]; + let eventLeaderboardEntry; + let eventLeaderboardEntryCount; + + describe('findEventLeaderboard', () => { + it('find empty withdraw db', async () => { + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(0); + eventLeaderboardEntry = await DBHelper.findEventLeaderboard({}); + eventLeaderboardEntry.length.should.equal(0); + }); + + it('should return existing entries if passing sort', async () => { + await DBHelper.insertEventLeaderboard(mockEventLeaderboard[0]); + await DBHelper.insertEventLeaderboard(mockEventLeaderboard[1]); + const foundEventLeaderboardEntry = await DBHelper.findEventLeaderboard({}, { investments: -1 }); + foundEventLeaderboardEntry.length.should.equal(2); + expect(foundEventLeaderboardEntry[0]).excluding('_id').to.deep.equal(mockEventLeaderboard[1]); + expect(foundEventLeaderboardEntry[1]).excluding('_id').to.deep.equal(mockEventLeaderboard[0]); + }); + + it('should return existing entries if not passing sort', async () => { + const foundEventLeaderboardEntry = await DBHelper.findEventLeaderboard({ userAddress: mockEventLeaderboard[0].userAddress }); + foundEventLeaderboardEntry.length.should.equal(1); + expect(foundEventLeaderboardEntry[0]).excluding('_id').to.deep.equal(mockEventLeaderboard[0]); + }); + }); + + describe('find one entry', () => { + it('find existed entry', async () => { + eventLeaderboardEntry = await DBHelper.findOneEventLeaderboard({ + userAddress: mockEventLeaderboard[0].userAddress, + eventAddress: mockEventLeaderboard[0].eventAddress, + }); + should.exist(eventLeaderboardEntry); + expect(eventLeaderboardEntry).excluding('_id').to.deep.equal(mockEventLeaderboard[0]); + }); + + it('should find null when txid is not existed', async () => { + eventLeaderboardEntry = await DBHelper.findOneEventLeaderboard({ eventAddress: '0x12333' }); + should.not.exist(eventLeaderboardEntry); + }); + }); + + describe('count eventLeaderboard', () => { + it('eventLeaderboard counts should be right', async () => { + eventLeaderboardEntryCount = await DBHelper.countEventLeaderboard({}); + eventLeaderboardEntryCount.should.equal(2); + }); + }); + + describe('insert eventLeaderboard', () => { + it('should insert new eventLeaderboard', async () => { + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(2); + await DBHelper.insertEventLeaderboard(mockEventLeaderboard[2]); + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(3); + eventLeaderboardEntry = await DBHelper.findOneEventLeaderboard({ + userAddress: mockEventLeaderboard[2].userAddress, + eventAddress: mockEventLeaderboard[2].eventAddress, + }); + should.exist(eventLeaderboardEntry); + expect(eventLeaderboardEntry).excluding('_id').to.deep.equal(mockEventLeaderboard[2]); + }); + + it('should update investment if insert existed entry', async () => { + const newEntry = { + eventAddress: '0x12345ea6e4e1f5375f7596b73b3b597e6507201a', + userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', + investments: '2000000', + winnings: '0', + returnRatio: '0', + }; + const expectedEntry = { + eventAddress: '0x12345ea6e4e1f5375f7596b73b3b597e6507201a', + userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', + investments: '4000000', + winnings: '0', + returnRatio: '0', + }; + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(3); + await DBHelper.insertEventLeaderboard(newEntry); + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(3); + eventLeaderboardEntry = await DBHelper.findOneEventLeaderboard({ + userAddress: newEntry.userAddress, + eventAddress: newEntry.eventAddress, + }); + should.exist(eventLeaderboardEntry); + expect(eventLeaderboardEntry).excluding(['_id']).to.deep.equal(expectedEntry); + }); + + it('should update winnings if insert existed entry', async () => { + const newEntry = { + eventAddress: '0x12345ea6e4e1f5375f7596b73b3b597e6507201a', + userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', + investments: '0', + winnings: '2000000', + returnRatio: '0', + }; + const expectedEntry = { + eventAddress: '0x12345ea6e4e1f5375f7596b73b3b597e6507201a', + userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', + investments: '4000000', + winnings: '2000000', + returnRatio: '0.5', + }; + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(3); + await DBHelper.insertEventLeaderboard(newEntry); + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(3); + eventLeaderboardEntry = await DBHelper.findOneEventLeaderboard({ + userAddress: newEntry.userAddress, + eventAddress: newEntry.eventAddress, + }); + should.exist(eventLeaderboardEntry); + expect(eventLeaderboardEntry).excluding(['_id']).to.deep.equal(expectedEntry); + }); + }); + + describe('update eventLeaderboard', () => { + it('should update eventLeaderboard when txid is existed', async () => { + const newEntry = { + eventAddress: '0x12345ea6e4e1f5375f7596b73b3b597e6507201a', + userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', + investments: '0', + winnings: '2000000', + returnRatio: '0', + }; + eventLeaderboardEntry = await DBHelper.findOneEventLeaderboard({ + userAddress: mockEventLeaderboard[2].userAddress, + eventAddress: mockEventLeaderboard[2].eventAddress, + }); + expect(eventLeaderboardEntry).excluding(['_id']).to.deep.not.equal(newEntry); + await DBHelper.updateEventLeaderboard(newEntry); + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(3); + eventLeaderboardEntry = await DBHelper.findOneEventLeaderboard({ + userAddress: mockEventLeaderboard[2].userAddress, + eventAddress: mockEventLeaderboard[2].eventAddress, + }); + should.exist(eventLeaderboardEntry); + expect(eventLeaderboardEntry).excluding(['_id']).to.deep.equal(newEntry); + }); + + it('should not update withdraw when eventAddress is not existed', async () => { + const newEntry = { + eventAddress: '0x12345', + userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', + investments: '0', + winnings: '2000000', + returnRatio: '0', + }; + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(3); + await DBHelper.updateEventLeaderboard(newEntry); + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(3); + eventLeaderboardEntry = await DBHelper.findOneEventLeaderboard({ + userAddress: newEntry.userAddress, + eventAddress: newEntry.eventAddress, + }); + should.not.exist(eventLeaderboardEntry); + }); + + it('should not update withdraw when userAddress is not existed', async () => { + const newEntry = { + eventAddress: '0x12345ea6e4e1f5375f7596b73b3b597e6507201a', + userAddress: '0x12345', + investments: '0', + winnings: '2000000', + returnRatio: '0', + }; + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(3); + await DBHelper.updateEventLeaderboard(newEntry); + eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); + eventLeaderboardEntryCount.should.equal(3); + eventLeaderboardEntry = await DBHelper.findOneEventLeaderboard({ + userAddress: newEntry.userAddress, + eventAddress: newEntry.eventAddress, + }); + should.not.exist(eventLeaderboardEntry); + }); + }); + }); + + /* GlobalLeaderboard */ + describe('test GlobalLeaderboard db', () => { + const mockGlobalLeaderboard = [ + { + userAddress: '0x939592864c0bd3355b2d54e4fa2203e8343b6d6a', + investments: '1000000000', + winnings: '1000000000', + returnRatio: '1', + }, + { + userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', + investments: '5000000', + winnings: '5000000', + returnRatio: '1', + }, + { + userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', + investments: '2000000', + winnings: '0', + returnRatio: '0', + }, + ]; + let globalLeaderboardEntry; + let globalLeaderboardEntryCount; + + describe('findGlobalLeaderboard', () => { + it('find empty globalLeaderboard db', async () => { + globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); + globalLeaderboardEntryCount.should.equal(0); + globalLeaderboardEntry = await DBHelper.findGlobalLeaderboard({}); + globalLeaderboardEntry.length.should.equal(0); + }); + + it('should return existing entries if passing sort', async () => { + await DBHelper.insertGlobalLeaderboard(mockGlobalLeaderboard[0]); + await DBHelper.insertGlobalLeaderboard(mockGlobalLeaderboard[1]); + const foundGlobalLeaderboardEntry = await DBHelper.findGlobalLeaderboard({}, { investments: -1 }); + foundGlobalLeaderboardEntry.length.should.equal(2); + expect(foundGlobalLeaderboardEntry[0]).excluding('_id').to.deep.equal(mockGlobalLeaderboard[1]); + expect(foundGlobalLeaderboardEntry[1]).excluding('_id').to.deep.equal(mockGlobalLeaderboard[0]); + }); + + it('should return existing entries if not passing sort', async () => { + const foundGlobalLeaderboardEntry = await DBHelper.findGlobalLeaderboard({ userAddress: mockGlobalLeaderboard[0].userAddress }); + foundGlobalLeaderboardEntry.length.should.equal(1); + expect(foundGlobalLeaderboardEntry[0]).excluding('_id').to.deep.equal(mockGlobalLeaderboard[0]); + }); + }); + + describe('find one entry', () => { + it('find existed entry', async () => { + globalLeaderboardEntry = await DBHelper.findOneGlobalLeaderboard({ userAddress: mockGlobalLeaderboard[0].userAddress }); + should.exist(globalLeaderboardEntry); + expect(globalLeaderboardEntry).excluding('_id').to.deep.equal(mockGlobalLeaderboard[0]); + }); + + it('should find null when txid is not existed', async () => { + globalLeaderboardEntry = await DBHelper.findOneGlobalLeaderboard({ userAddress: '0x123' }); + should.not.exist(globalLeaderboardEntry); + }); + }); + + describe('count globalLeaderboard', () => { + it('globalLeaderboard counts should be right', async () => { + globalLeaderboardEntryCount = await DBHelper.countGlobalLeaderboard({}); + globalLeaderboardEntryCount.should.equal(2); + }); + }); + + describe('insert globalLeaderboard', () => { + it('should insert new globalLeaderboard', async () => { + globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); + globalLeaderboardEntryCount.should.equal(2); + await DBHelper.insertGlobalLeaderboard(mockGlobalLeaderboard[2]); + globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); + globalLeaderboardEntryCount.should.equal(3); + globalLeaderboardEntry = await DBHelper.findOneGlobalLeaderboard({ userAddress: mockGlobalLeaderboard[2].userAddress }); + should.exist(globalLeaderboardEntry); + expect(globalLeaderboardEntry).excluding('_id').to.deep.equal(mockGlobalLeaderboard[2]); + }); + + it('should update investment if insert existed entry', async () => { + const newEntry = { + userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', + investments: '2000000', + winnings: '0', + returnRatio: '0', + }; + const expectedEntry = { + userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', + investments: '4000000', + winnings: '0', + returnRatio: '0', + }; + globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); + globalLeaderboardEntryCount.should.equal(3); + await DBHelper.insertGlobalLeaderboard(newEntry); + globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); + globalLeaderboardEntryCount.should.equal(3); + globalLeaderboardEntry = await DBHelper.findOneGlobalLeaderboard({ userAddress: newEntry.userAddress }); + should.exist(globalLeaderboardEntry); + expect(globalLeaderboardEntry).excluding(['_id']).to.deep.equal(expectedEntry); + }); + + it('should update winnings if insert existed entry', async () => { + const newEntry = { + userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', + investments: '0', + winnings: '2000000', + returnRatio: '0', + }; + const expectedEntry = { + userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', + investments: '4000000', + winnings: '2000000', + returnRatio: '0.5', + }; + globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); + globalLeaderboardEntryCount.should.equal(3); + await DBHelper.insertGlobalLeaderboard(newEntry); + globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); + globalLeaderboardEntryCount.should.equal(3); + globalLeaderboardEntry = await DBHelper.findOneGlobalLeaderboard({ userAddress: newEntry.userAddress }); + should.exist(globalLeaderboardEntry); + expect(globalLeaderboardEntry).excluding(['_id']).to.deep.equal(expectedEntry); + }); + }); + + describe('update globalLeaderboard', () => { + it('should update globalLeaderboard when userAddress is existed', async () => { + const newEntry = { + userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', + investments: '0', + winnings: '2000000', + returnRatio: '0', + }; + globalLeaderboardEntry = await DBHelper.findOneGlobalLeaderboard({ userAddress: mockGlobalLeaderboard[2].userAddress }); + expect(globalLeaderboardEntry).excluding(['_id']).to.deep.not.equal(newEntry); + await DBHelper.updateGlobalLeaderboard(newEntry); + globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); + globalLeaderboardEntryCount.should.equal(3); + globalLeaderboardEntry = await DBHelper.findOneGlobalLeaderboard({ userAddress: mockGlobalLeaderboard[2].userAddress }); + should.exist(globalLeaderboardEntry); + expect(globalLeaderboardEntry).excluding(['_id']).to.deep.equal(newEntry); + }); + + it('should not update withdraw when userAddress is not existed', async () => { + const newEntry = { + userAddress: '0x12345', + investments: '0', + winnings: '2000000', + returnRatio: '0', + }; + globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); + globalLeaderboardEntryCount.should.equal(3); + await DBHelper.updateGlobalLeaderboard(newEntry); + globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); + globalLeaderboardEntryCount.should.equal(3); + globalLeaderboardEntry = await DBHelper.findOneGlobalLeaderboard({ userAddress: newEntry.userAddress }); + should.not.exist(globalLeaderboardEntry); + }); + }); + }); + after(async () => { fs.removeSync(getDbDir()); }); diff --git a/test/graphql/queries.tests.js b/test/graphql/queries.tests.js index e42799f5..87e4d0ac 100644 --- a/test/graphql/queries.tests.js +++ b/test/graphql/queries.tests.js @@ -5,6 +5,7 @@ const { PAGINATED_BETS, PAGINATED_RESULT_SETS, PAGINATED_WITHDRAWS, + PAGINATED_LEADERBOARD, } = require('./schema-helper'); const schema = require('../../src/graphql/schema'); @@ -447,4 +448,80 @@ describe('graphql/queries', () => { tester.test(true, valid); }); }); + + describe('eventLeaderboard', () => { + it('should return the query', async () => { + const valid = ` + query { + eventLeaderboard(filter: { + userAddress:"0x939592864c0bd3355b2d54e4fa2203e8343b6d6a", + eventAddress:"0x09645ea6e4e1f5375f7596b73b3b597e6507201a" + }) { + ${PAGINATED_LEADERBOARD} + } + } + `; + tester.test(true, valid); + }); + + it('should accept the eventAddress filter', async () => { + const valid = ` + query { + eventLeaderboard(filter: { eventAddress: "0x09645ea6e4e1f5375f7596b73b3b597e6507201a" }) { + ${PAGINATED_LEADERBOARD} + } + } + `; + tester.test(true, valid); + }); + + it('should accept the userAddress filter', async () => { + const valid = ` + query { + eventLeaderboard(filter: { userAddress:"0x939592864c0bd3355b2d54e4fa2203e8343b6d6a" }) { + ${PAGINATED_LEADERBOARD} + } + } + `; + tester.test(true, valid); + }); + }); + + describe('globalLeaderboard', () => { + it('should return the query', async () => { + const valid = ` + query { + globalLeaderboard(filter: { + userAddress:"0x939592864c0bd3355b2d54e4fa2203e8343b6d6a", + eventAddress:"0x09645ea6e4e1f5375f7596b73b3b597e6507201a" + }) { + ${PAGINATED_LEADERBOARD} + } + } + `; + tester.test(true, valid); + }); + + it('should accept the eventAddress filter', async () => { + const valid = ` + query { + globalLeaderboard(filter: { eventAddress: "0x09645ea6e4e1f5375f7596b73b3b597e6507201a" }) { + ${PAGINATED_LEADERBOARD} + } + } + `; + tester.test(true, valid); + }); + + it('should accept the userAddress filter', async () => { + const valid = ` + query { + globalLeaderboard(filter: { userAddress:"0x939592864c0bd3355b2d54e4fa2203e8343b6d6a" }) { + ${PAGINATED_LEADERBOARD} + } + } + `; + tester.test(true, valid); + }); + }); }); diff --git a/test/graphql/schema-helper.js b/test/graphql/schema-helper.js index 94638297..78c8a9eb 100644 --- a/test/graphql/schema-helper.js +++ b/test/graphql/schema-helper.js @@ -141,6 +141,22 @@ const PAGINATED_WITHDRAWS = ` } `; +const LEADERBOARD = ` + eventAddress + userAddress + investments + winnings + returnRatio +`; + +const PAGINATED_LEADERBOARD = ` + totalCount + ${PAGE_INFO} + items { + ${LEADERBOARD} + } +`; + module.exports = { PAGE_INFO, BLOCK, @@ -153,4 +169,5 @@ module.exports = { BET, RESULT_SET, WITHDRAW, + PAGINATED_LEADERBOARD, }; From 21298e585a311dbb2953d7624a594f5cc0faeead Mon Sep 17 00:00:00 2001 From: Bernard Date: Mon, 8 Jul 2019 14:42:11 -0700 Subject: [PATCH 13/24] Changes requested --- src/graphql/queries/index.js | 6 ++--- src/graphql/queries/leaderboard.js | 10 ++++---- src/graphql/queries/utils.js | 1 + src/graphql/schema.js | 22 ++++++++--------- src/models/event-leaderboard.js | 2 +- src/models/global-leaderboard.js | 2 +- test/db/index.js | 38 +++++++++++++++--------------- 7 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/graphql/queries/index.js b/src/graphql/queries/index.js index 502d19b4..1f0283bc 100644 --- a/src/graphql/queries/index.js +++ b/src/graphql/queries/index.js @@ -10,7 +10,7 @@ const allStats = require('./all-stats'); const mostBets = require('./most-bets'); const biggestWinners = require('./biggest-winners'); const withdrawableEvents = require('./withdrawable-events'); -const { eventLeaderboard, globalLeaderboard } = require('./leaderboard'); +const { eventLeaderboardEntries, globalLeaderboardEntries } = require('./leaderboard'); module.exports = { events, @@ -25,6 +25,6 @@ module.exports = { mostBets, biggestWinners, withdrawableEvents, - eventLeaderboard, - globalLeaderboard, + eventLeaderboardEntries, + globalLeaderboardEntries, }; diff --git a/src/graphql/queries/leaderboard.js b/src/graphql/queries/leaderboard.js index eb329c3f..b40c1dbe 100644 --- a/src/graphql/queries/leaderboard.js +++ b/src/graphql/queries/leaderboard.js @@ -6,7 +6,7 @@ const buildFilters = ({ eventAddress, } = {}) => { if (!eventAddress && !userAddress) { - throw Error('eventAddress or userAddress missing in filters'); + throw Error('eventAddress and/or userAddress missing in filters'); } const filters = []; const filter = {}; @@ -17,7 +17,7 @@ const buildFilters = ({ return filters; }; -const eventLeaderboard = async ( +const eventLeaderboardEntries = async ( root, { filter, orderBy, limit, skip }, { db: { EventLeaderboard } }, @@ -32,7 +32,7 @@ const eventLeaderboard = async ( }); }; -const globalLeaderboard = async ( +const globalLeaderboardEntries = async ( root, { filter, orderBy, limit, skip }, { db: { GlobalLeaderboard } }, @@ -48,6 +48,6 @@ const globalLeaderboard = async ( }; module.exports = { - eventLeaderboard, - globalLeaderboard, + eventLeaderboardEntries, + globalLeaderboardEntries, }; diff --git a/src/graphql/queries/utils.js b/src/graphql/queries/utils.js index cae29da3..33f6be09 100644 --- a/src/graphql/queries/utils.js +++ b/src/graphql/queries/utils.js @@ -27,6 +27,7 @@ const lowercaseFilters = (filters) => { 'centralizedOracleAddress', 'winnerAddress', 'transactorAddress', + 'userAddress', ]; // Loop through each filter diff --git a/src/graphql/schema.js b/src/graphql/schema.js index 284724da..de628682 100644 --- a/src/graphql/schema.js +++ b/src/graphql/schema.js @@ -239,18 +239,18 @@ type PaginatedBiggestWinner { items: [BiggestWinner]! } -type Leaderboard { +type LeaderboardEntry { eventAddress: String userAddress: String! investments: String! winnings: String! - returnRatio: String! + returnRatio: Float! } -type PaginatedLeaderboard { +type PaginatedLeaderboardEntry { totalCount: Int! pageInfo: PageInfo - items: [Leaderboard]! + items: [LeaderboardEntry]! } input Order { @@ -258,7 +258,7 @@ input Order { direction: OrderDirection! } -input LeaderboardFilter { +input LeaderboardEntryFilter { userAddress: String eventAddress: String } @@ -418,19 +418,19 @@ type Query { skip: Int ): PaginatedBiggestWinner! - eventLeaderboard( - filter: LeaderboardFilter + eventLeaderboardEntries( + filter: LeaderboardEntryFilter orderBy: [Order!] limit: Int skip: Int - ): PaginatedLeaderboard! + ): PaginatedLeaderboardEntry! - globalLeaderboard( - filter: LeaderboardFilter + globalLeaderboardEntries( + filter: LeaderboardEntryFilter orderBy: [Order!] limit: Int skip: Int - ): PaginatedLeaderboard! + ): PaginatedLeaderboardEntry! } type Mutation { diff --git a/src/models/event-leaderboard.js b/src/models/event-leaderboard.js index 43c66e60..3754bcd4 100644 --- a/src/models/event-leaderboard.js +++ b/src/models/event-leaderboard.js @@ -21,6 +21,6 @@ module.exports = class EventLeaderboard { this.investments = params.investments; this.winnings = params.winnings; const ratio = new BigNumber(this.winnings).dividedBy(new BigNumber(this.investments)); - this.returnRatio = ratio.toString(); + this.returnRatio = ratio.toNumber(); } }; diff --git a/src/models/global-leaderboard.js b/src/models/global-leaderboard.js index 5f26aa5c..64c94612 100644 --- a/src/models/global-leaderboard.js +++ b/src/models/global-leaderboard.js @@ -19,6 +19,6 @@ module.exports = class GlobalLeaderboard { this.investments = params.investments; this.winnings = params.winnings; const ratio = new BigNumber(this.winnings).dividedBy(new BigNumber(this.investments)); - this.returnRatio = ratio.toString(); + this.returnRatio = ratio.toNumber(); } }; diff --git a/test/db/index.js b/test/db/index.js index cac9f97d..8d9ed2c7 100644 --- a/test/db/index.js +++ b/test/db/index.js @@ -1779,21 +1779,21 @@ describe('db', () => { userAddress: '0x939592864c0bd3355b2d54e4fa2203e8343b6d6a', investments: '1000000000', winnings: '1000000000', - returnRatio: '1', + returnRatio: 1, }, { eventAddress: '0x09645ea6e4e1f5375f7596b73b3b597e6507201a', userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', investments: '5000000', winnings: '5000000', - returnRatio: '1', + returnRatio: 1, }, { eventAddress: '0x12345ea6e4e1f5375f7596b73b3b597e6507201a', userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', investments: '2000000', winnings: '0', - returnRatio: '0', + returnRatio: 0, }, ]; let eventLeaderboardEntry; @@ -1867,14 +1867,14 @@ describe('db', () => { userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', investments: '2000000', winnings: '0', - returnRatio: '0', + returnRatio: 0, }; const expectedEntry = { eventAddress: '0x12345ea6e4e1f5375f7596b73b3b597e6507201a', userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', investments: '4000000', winnings: '0', - returnRatio: '0', + returnRatio: 0, }; eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); eventLeaderboardEntryCount.should.equal(3); @@ -1895,14 +1895,14 @@ describe('db', () => { userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', investments: '0', winnings: '2000000', - returnRatio: '0', + returnRatio: 0, }; const expectedEntry = { eventAddress: '0x12345ea6e4e1f5375f7596b73b3b597e6507201a', userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', investments: '4000000', winnings: '2000000', - returnRatio: '0.5', + returnRatio: 0.5, }; eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); eventLeaderboardEntryCount.should.equal(3); @@ -1925,7 +1925,7 @@ describe('db', () => { userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', investments: '0', winnings: '2000000', - returnRatio: '0', + returnRatio: 0, }; eventLeaderboardEntry = await DBHelper.findOneEventLeaderboard({ userAddress: mockEventLeaderboard[2].userAddress, @@ -1949,7 +1949,7 @@ describe('db', () => { userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', investments: '0', winnings: '2000000', - returnRatio: '0', + returnRatio: 0, }; eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); eventLeaderboardEntryCount.should.equal(3); @@ -1969,7 +1969,7 @@ describe('db', () => { userAddress: '0x12345', investments: '0', winnings: '2000000', - returnRatio: '0', + returnRatio: 0, }; eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); eventLeaderboardEntryCount.should.equal(3); @@ -1992,19 +1992,19 @@ describe('db', () => { userAddress: '0x939592864c0bd3355b2d54e4fa2203e8343b6d6a', investments: '1000000000', winnings: '1000000000', - returnRatio: '1', + returnRatio: 1, }, { userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', investments: '5000000', winnings: '5000000', - returnRatio: '1', + returnRatio: 1, }, { userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', investments: '2000000', winnings: '0', - returnRatio: '0', + returnRatio: 0, }, ]; let globalLeaderboardEntry; @@ -2071,13 +2071,13 @@ describe('db', () => { userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', investments: '2000000', winnings: '0', - returnRatio: '0', + returnRatio: 0, }; const expectedEntry = { userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', investments: '4000000', winnings: '0', - returnRatio: '0', + returnRatio: 0, }; globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); globalLeaderboardEntryCount.should.equal(3); @@ -2094,13 +2094,13 @@ describe('db', () => { userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', investments: '0', winnings: '2000000', - returnRatio: '0', + returnRatio: 0, }; const expectedEntry = { userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', investments: '4000000', winnings: '2000000', - returnRatio: '0.5', + returnRatio: 0.5, }; globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); globalLeaderboardEntryCount.should.equal(3); @@ -2119,7 +2119,7 @@ describe('db', () => { userAddress: '0x123456764c0bd3355b2d54e4fa2203e834111111', investments: '0', winnings: '2000000', - returnRatio: '0', + returnRatio: 0, }; globalLeaderboardEntry = await DBHelper.findOneGlobalLeaderboard({ userAddress: mockGlobalLeaderboard[2].userAddress }); expect(globalLeaderboardEntry).excluding(['_id']).to.deep.not.equal(newEntry); @@ -2136,7 +2136,7 @@ describe('db', () => { userAddress: '0x12345', investments: '0', winnings: '2000000', - returnRatio: '0', + returnRatio: 0, }; globalLeaderboardEntryCount = await db.GlobalLeaderboard.count({}); globalLeaderboardEntryCount.should.equal(3); From 9e61c682f1cdf969605ba7db8558d16cdce38dcd Mon Sep 17 00:00:00 2001 From: Bernard Date: Mon, 8 Jul 2019 17:33:47 -0700 Subject: [PATCH 14/24] Rewrite leaderboard update sync --- src/sync/update-leaderboard.js | 88 ++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/src/sync/update-leaderboard.js b/src/sync/update-leaderboard.js index 85e7e0dd..3d612da6 100644 --- a/src/sync/update-leaderboard.js +++ b/src/sync/update-leaderboard.js @@ -1,4 +1,5 @@ const { each, isNull, find } = require('lodash'); +const async = require('async'); const web3 = require('../web3'); const { TX_STATUS } = require('../constants'); const EventSig = require('../config/event-sig'); @@ -26,6 +27,42 @@ const getInvestments = async (txs) => { return accumulated; }; +const getWinnings = async (eventAddress, address, investments, callback) => { + try { + const res = await MultipleResultsEventApi.calculateWinnings({ + eventAddress, + address, + }); + const winnings = res.toString(10); + await insertLeaderboards(eventAddress, address, winnings, investments, callback); + } catch (err) { + throw err; + } +}; + +const insertLeaderboards = async (eventAddress, userAddress, winnings, investments, callback) => { + try { + // update event leaderboard winnings + const eventLeaderboardEntry = new EventLeaderboard({ + eventAddress, + userAddress, + investments: '0', // event leaderboard entry already has user's investments + winnings, + }); + await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); + // update global leaderboard investments, winnings + const globalLeaderboard = new GlobalLeaderboard({ + userAddress, + investments, + winnings, + }); + await DBHelper.insertGlobalLeaderboard(globalLeaderboard); + callback(); + } catch (err) { + throw err; + } +}; + const updateLeaderboardWithdrawing = async (syncPromises, events, limit) => { each(events, async (event) => { syncPromises.push(limit(async (eventObj) => { @@ -51,44 +88,21 @@ const updateLeaderboardWithdrawing = async (syncPromises, events, limit) => { filtered.push(tx); } }); - // calculate winnings for all participants - const promises = []; - each(filtered, (tx) => { - promises.push(new Promise(async (resolve, reject) => { - try { - const { eventAddress, betterAddress, resultIndex, centralizedOracleAddress } = tx; - const userAddress = betterAddress || centralizedOracleAddress; - // calculate winning for this user in this event - let winnings = '0'; - if (resultIndex === eventObj.currentResultIndex) { - const res = await MultipleResultsEventApi.calculateWinnings({ - eventAddress, - address: userAddress, - }); - winnings = res.toString(10); - } - // update event leaderboard winnings - const eventLeaderboardEntry = new EventLeaderboard({ - eventAddress, - userAddress, - investments: '0', // event leaderboard entry already has user's investments - winnings, - }); - await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); - // update global leaderboard investments, winnings - const globalLeaderboard = new GlobalLeaderboard({ - userAddress, - investments: investments[userAddress], - winnings, - }); - await DBHelper.insertGlobalLeaderboard(globalLeaderboard); - resolve(); - } catch (err) { - reject(err); - } - })); + async.forEachOf(filtered, (value, key, callback) => { + try { + const { eventAddress, betterAddress, centralizedOracleAddress } = value; + const userAddress = betterAddress || centralizedOracleAddress; + // calculate winning for this user in this event + getWinnings(eventAddress, userAddress, investments[userAddress], callback); + } catch (err) { + callback(err); + } + }, (err) => { + if (err) { + logger.error(err.message); + throw err; + } }); - await Promise.all(promises); } catch (err) { logger.error(`UPDATE leaderboard when withdrawing status changed error: ${err.message}`); throw err; From 934256af2785f3b3f706e001d52f3e25c75c4e6c Mon Sep 17 00:00:00 2001 From: Bernard Date: Tue, 9 Jul 2019 14:56:36 -0700 Subject: [PATCH 15/24] Change to use each of series --- src/sync/update-leaderboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sync/update-leaderboard.js b/src/sync/update-leaderboard.js index 3d612da6..b13abccf 100644 --- a/src/sync/update-leaderboard.js +++ b/src/sync/update-leaderboard.js @@ -88,7 +88,7 @@ const updateLeaderboardWithdrawing = async (syncPromises, events, limit) => { filtered.push(tx); } }); - async.forEachOf(filtered, (value, key, callback) => { + async.eachOfSeries(filtered, (value, key, callback) => { try { const { eventAddress, betterAddress, centralizedOracleAddress } = value; const userAddress = betterAddress || centralizedOracleAddress; From b3f5499fdedff5cf22b100ba02b238aef95127f8 Mon Sep 17 00:00:00 2001 From: Bernard Date: Tue, 9 Jul 2019 16:22:48 -0700 Subject: [PATCH 16/24] Make leaderboard query sort properly --- src/graphql/queries/leaderboard.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/graphql/queries/leaderboard.js b/src/graphql/queries/leaderboard.js index b40c1dbe..72a8aaea 100644 --- a/src/graphql/queries/leaderboard.js +++ b/src/graphql/queries/leaderboard.js @@ -1,4 +1,5 @@ const { isArray, each } = require('lodash'); +const BigNumber = require('bignumber.js'); const { lowercaseFilters, runPaginatedQuery } = require('./utils'); const buildFilters = ({ @@ -23,13 +24,20 @@ const eventLeaderboardEntries = async ( { db: { EventLeaderboard } }, ) => { const query = filter ? { $or: buildFilters(lowercaseFilters(filter)) } : {}; - return runPaginatedQuery({ + const res = await runPaginatedQuery({ db: EventLeaderboard, filter: query, orderBy, limit, skip, }); + + if (orderBy && orderBy.length > 0) { + const { field } = orderBy[0]; + res.items.sort((a, b) => new BigNumber(b[field]).comparedTo(new BigNumber(a[field]))); // all descending + } + + return res; }; const globalLeaderboardEntries = async ( @@ -38,13 +46,20 @@ const globalLeaderboardEntries = async ( { db: { GlobalLeaderboard } }, ) => { const query = filter ? { $or: buildFilters(lowercaseFilters(filter)) } : {}; - return runPaginatedQuery({ + const res = await runPaginatedQuery({ db: GlobalLeaderboard, filter: query, orderBy, limit, skip, }); + + if (orderBy && orderBy.length > 0) { + const { field } = orderBy[0]; + res.items.sort((a, b) => new BigNumber(b[field]).comparedTo(new BigNumber(a[field]))); // all descending + } + + return res; }; module.exports = { From c12230fdd3562f6bfa6a94b383b769364295688d Mon Sep 17 00:00:00 2001 From: Bowen Zhang Date: Mon, 8 Jul 2019 15:38:00 -0700 Subject: [PATCH 17/24] Add migration for eventleaderboard --- src/db/migrations/migration2.js | 100 ++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/db/migrations/migration2.js diff --git a/src/db/migrations/migration2.js b/src/db/migrations/migration2.js new file mode 100644 index 00000000..37016607 --- /dev/null +++ b/src/db/migrations/migration2.js @@ -0,0 +1,100 @@ +const { uniqBy } = require('lodash'); +const pLimit = require('p-limit'); +const async = require('async'); +const logger = require('../../utils/logger'); +const DBHelper = require('../db-helper'); +const { TX_STATUS, EVENT_STATUS } = require('../../constants'); +const MultipleResultsEventApi = require('../../api/multiple-results-event'); +const EventLeaderboard = require('../../models/event-leaderboard'); + +const PROMISE_CONCURRENCY_LIMIT = 15; +const limit = pLimit(PROMISE_CONCURRENCY_LIMIT); +async function helper (dbMethod, callback) { + await dbMethod(); + callback(); +} +async function migration2(next) { + try { + // migrate event leaderboard + const events = await DBHelper.findEvent({ txStatus: TX_STATUS.SUCCESS }); + const promises = []; + + events.forEach(async (event, key) => { + const eventAddress = event.address; + const resultSet = await DBHelper.findResultSet({ eventRound: 0, txStatus: TX_STATUS.SUCCESS, eventAddress: event.address }); + const bets = await DBHelper.findBet({ + eventAddress: event.address, + txStatus: TX_STATUS.SUCCESS, + }); + const txs = resultSet.concat(bets); + + try { + let i = 0; + await async.whilst( + check => check(null, i < txs.length), // trigger iter + (next) => { + const tx = txs[i]; + i++; + + try { + const userAddress = tx.betterAddress || tx.centralizedOracleAddress; + const eventLeaderboardEntry = new EventLeaderboard({ + eventAddress, + userAddress, + investments: tx.amount, // event leaderboard entry already has user's investments + winnings: '0', + }); + helper(() => DBHelper.insertEventLeaderboard(eventLeaderboardEntry),()=>next(null, i)) + } catch (err) { + next(err, i); // err met, trigger the callback to end this loop + } + }, + ); + } catch (err) { + // will only be called if should end this loop + logger.error(err); + throw err; + } + if (event.status === EVENT_STATUS.WITHDRAWING) { + const addresses = new Set(); + txs.forEach((tx, index) => { + if(tx.resultIndex !== event.currentResultIndex) return; + if(tx.betterAddress) addresses.add(tx.betterAddress); + if(tx.centralizedOracleAddress) addresses.add(tx.centralizedOracleAddress); + }) + addresses.forEach((address, index) => { + promises.push(limit(async () => { + try { + // calculate winning for this user in this event + let winnings = '0'; + const res = await MultipleResultsEventApi.calculateWinnings({ + eventAddress, + address: address, + }); + winnings = res.toString(10); + // update event leaderboard winnings + const eventLeaderboardEntry = new EventLeaderboard({ + eventAddress, + userAddress: address, + investments: '0', // event leaderboard entry already has user's investments + winnings, + }); + await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); + } catch (err) { + logger.error('Migrate event leaderboard error:', err); + } + })); + }); + } + }); + + await Promise.all(promises); + logger.info('Migration 2 done'); + next(); + } catch (err) { + logger.error(`Migration 2 error: ${err.message}`); + throw err; + } +} + +module.exports = migration2; From 6aaa6d0f19cbd1461e2d8f8333a8ab5e32920ea2 Mon Sep 17 00:00:00 2001 From: Bowen Zhang Date: Tue, 9 Jul 2019 13:53:05 -0700 Subject: [PATCH 18/24] Migrate for global leaderboard --- src/db/migrations/migration2.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/db/migrations/migration2.js b/src/db/migrations/migration2.js index 37016607..df40f586 100644 --- a/src/db/migrations/migration2.js +++ b/src/db/migrations/migration2.js @@ -1,4 +1,3 @@ -const { uniqBy } = require('lodash'); const pLimit = require('p-limit'); const async = require('async'); const logger = require('../../utils/logger'); @@ -6,10 +5,11 @@ const DBHelper = require('../db-helper'); const { TX_STATUS, EVENT_STATUS } = require('../../constants'); const MultipleResultsEventApi = require('../../api/multiple-results-event'); const EventLeaderboard = require('../../models/event-leaderboard'); +const GlobalLeaderboard = require('../../models/global-leaderboard'); const PROMISE_CONCURRENCY_LIMIT = 15; const limit = pLimit(PROMISE_CONCURRENCY_LIMIT); -async function helper (dbMethod, callback) { +async function wrapper (dbMethod, callback) { await dbMethod(); callback(); } @@ -35,7 +35,6 @@ async function migration2(next) { (next) => { const tx = txs[i]; i++; - try { const userAddress = tx.betterAddress || tx.centralizedOracleAddress; const eventLeaderboardEntry = new EventLeaderboard({ @@ -44,7 +43,18 @@ async function migration2(next) { investments: tx.amount, // event leaderboard entry already has user's investments winnings: '0', }); - helper(() => DBHelper.insertEventLeaderboard(eventLeaderboardEntry),()=>next(null, i)) + wrapper(async () => { + await DBHelper.insertEventLeaderboard(eventLeaderboardEntry) + if(event.status === EVENT_STATUS.WITHDRAWING) { + const globalLeaderboardEntry = new GlobalLeaderboard({ + userAddress, + investments: tx.amount, + winnings: '0', + }) + await DBHelper.insertGlobalLeaderboard(globalLeaderboardEntry) + } + }, + ()=>next(null, i)) } catch (err) { next(err, i); // err met, trigger the callback to end this loop } @@ -80,6 +90,12 @@ async function migration2(next) { winnings, }); await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); + const globalLeaderboardEntry = new GlobalLeaderboard({ + userAddress: address, + investments: '0', + winnings, + }) + await DBHelper.insertGlobalLeaderboard(globalLeaderboardEntry) } catch (err) { logger.error('Migrate event leaderboard error:', err); } From 8b41d5a76bb9d6046bf34757c4e6917719426b27 Mon Sep 17 00:00:00 2001 From: Bowen Zhang Date: Wed, 10 Jul 2019 11:03:38 -0700 Subject: [PATCH 19/24] Move limit to outer loop --- src/db/migrations/migration2.js | 130 ++++++++++++++++---------------- 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/src/db/migrations/migration2.js b/src/db/migrations/migration2.js index df40f586..3e262609 100644 --- a/src/db/migrations/migration2.js +++ b/src/db/migrations/migration2.js @@ -9,84 +9,88 @@ const GlobalLeaderboard = require('../../models/global-leaderboard'); const PROMISE_CONCURRENCY_LIMIT = 15; const limit = pLimit(PROMISE_CONCURRENCY_LIMIT); -async function wrapper (dbMethod, callback) { + +async function wrapper(dbMethod, callback) { await dbMethod(); callback(); } + async function migration2(next) { try { // migrate event leaderboard const events = await DBHelper.findEvent({ txStatus: TX_STATUS.SUCCESS }); const promises = []; - events.forEach(async (event, key) => { - const eventAddress = event.address; - const resultSet = await DBHelper.findResultSet({ eventRound: 0, txStatus: TX_STATUS.SUCCESS, eventAddress: event.address }); - const bets = await DBHelper.findBet({ - eventAddress: event.address, - txStatus: TX_STATUS.SUCCESS, - }); - const txs = resultSet.concat(bets); + events.forEach((event, key) => { + promises.push(limit(async () => { + const eventAddress = event.address; + const resultSet = await DBHelper.findResultSet({ eventRound: 0, txStatus: TX_STATUS.SUCCESS, eventAddress: event.address }); + const bets = await DBHelper.findBet({ + eventAddress: event.address, + txStatus: TX_STATUS.SUCCESS, + }); + const txs = resultSet.concat(bets); - try { - let i = 0; - await async.whilst( - check => check(null, i < txs.length), // trigger iter - (next) => { - const tx = txs[i]; - i++; - try { - const userAddress = tx.betterAddress || tx.centralizedOracleAddress; - const eventLeaderboardEntry = new EventLeaderboard({ - eventAddress, - userAddress, - investments: tx.amount, // event leaderboard entry already has user's investments - winnings: '0', - }); - wrapper(async () => { - await DBHelper.insertEventLeaderboard(eventLeaderboardEntry) - if(event.status === EVENT_STATUS.WITHDRAWING) { - const globalLeaderboardEntry = new GlobalLeaderboard({ - userAddress, - investments: tx.amount, - winnings: '0', - }) - await DBHelper.insertGlobalLeaderboard(globalLeaderboardEntry) - } - }, - ()=>next(null, i)) - } catch (err) { - next(err, i); // err met, trigger the callback to end this loop - } - }, - ); - } catch (err) { + try { + let i = 0; + await async.whilst( + check => check(null, i < txs.length), // trigger iter + (next) => { + const tx = txs[i]; + i++; + try { + const userAddress = tx.betterAddress || tx.centralizedOracleAddress; + const eventLeaderboardEntry = new EventLeaderboard({ + eventAddress, + userAddress, + investments: tx.amount, + winnings: '0', + }); + wrapper( + async () => { + await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); + if (event.status === EVENT_STATUS.WITHDRAWING) { + const globalLeaderboardEntry = new GlobalLeaderboard({ + userAddress, + investments: tx.amount, + winnings: '0', + }); + await DBHelper.insertGlobalLeaderboard(globalLeaderboardEntry); + } + }, + () => next(null, i), + ); + } catch (err) { + next(err, i); // err met, trigger the callback to end this loop + } + }, + ); + } catch (err) { // will only be called if should end this loop - logger.error(err); - throw err; - } - if (event.status === EVENT_STATUS.WITHDRAWING) { - const addresses = new Set(); - txs.forEach((tx, index) => { - if(tx.resultIndex !== event.currentResultIndex) return; - if(tx.betterAddress) addresses.add(tx.betterAddress); - if(tx.centralizedOracleAddress) addresses.add(tx.centralizedOracleAddress); - }) - addresses.forEach((address, index) => { - promises.push(limit(async () => { + logger.error(err); + throw err; + } + + if (event.status === EVENT_STATUS.WITHDRAWING) { + const addresses = new Set(); + txs.forEach((tx, index) => { + if (tx.resultIndex !== event.currentResultIndex) return; + if (tx.betterAddress) addresses.add(tx.betterAddress); + if (tx.centralizedOracleAddress) addresses.add(tx.centralizedOracleAddress); + }); + async.eachOfSeries(addresses, async (address, index) => { try { // calculate winning for this user in this event - let winnings = '0'; const res = await MultipleResultsEventApi.calculateWinnings({ eventAddress, - address: address, + address, }); - winnings = res.toString(10); + const winnings = res.toString(10); // update event leaderboard winnings const eventLeaderboardEntry = new EventLeaderboard({ eventAddress, userAddress: address, - investments: '0', // event leaderboard entry already has user's investments + investments: '0', winnings, }); await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); @@ -94,14 +98,14 @@ async function migration2(next) { userAddress: address, investments: '0', winnings, - }) - await DBHelper.insertGlobalLeaderboard(globalLeaderboardEntry) + }); + await DBHelper.insertGlobalLeaderboard(globalLeaderboardEntry); } catch (err) { logger.error('Migrate event leaderboard error:', err); } - })); - }); - } + }); + } + })); }); await Promise.all(promises); From b625c60876691e543a27c2327b4eb0d933b30ef7 Mon Sep 17 00:00:00 2001 From: Bowen Zhang Date: Thu, 11 Jul 2019 10:32:09 -0700 Subject: [PATCH 20/24] Get rid of wrapper function --- src/db/migrations/migration2.js | 38 +++++++++++---------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/src/db/migrations/migration2.js b/src/db/migrations/migration2.js index 3e262609..243a82a7 100644 --- a/src/db/migrations/migration2.js +++ b/src/db/migrations/migration2.js @@ -10,11 +10,6 @@ const GlobalLeaderboard = require('../../models/global-leaderboard'); const PROMISE_CONCURRENCY_LIMIT = 15; const limit = pLimit(PROMISE_CONCURRENCY_LIMIT); -async function wrapper(dbMethod, callback) { - await dbMethod(); - callback(); -} - async function migration2(next) { try { // migrate event leaderboard @@ -35,33 +30,24 @@ async function migration2(next) { let i = 0; await async.whilst( check => check(null, i < txs.length), // trigger iter - (next) => { + async () => { const tx = txs[i]; i++; - try { - const userAddress = tx.betterAddress || tx.centralizedOracleAddress; - const eventLeaderboardEntry = new EventLeaderboard({ - eventAddress, + const userAddress = tx.betterAddress || tx.centralizedOracleAddress; + const eventLeaderboardEntry = new EventLeaderboard({ + eventAddress, + userAddress, + investments: tx.amount, + winnings: '0', + }); + await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); + if (event.status === EVENT_STATUS.WITHDRAWING) { + const globalLeaderboardEntry = new GlobalLeaderboard({ userAddress, investments: tx.amount, winnings: '0', }); - wrapper( - async () => { - await DBHelper.insertEventLeaderboard(eventLeaderboardEntry); - if (event.status === EVENT_STATUS.WITHDRAWING) { - const globalLeaderboardEntry = new GlobalLeaderboard({ - userAddress, - investments: tx.amount, - winnings: '0', - }); - await DBHelper.insertGlobalLeaderboard(globalLeaderboardEntry); - } - }, - () => next(null, i), - ); - } catch (err) { - next(err, i); // err met, trigger the callback to end this loop + await DBHelper.insertGlobalLeaderboard(globalLeaderboardEntry); } }, ); From a1ac0b48a29b3f4ed5c806e1ca798caec1987be2 Mon Sep 17 00:00:00 2001 From: Bowen Zhang Date: Thu, 11 Jul 2019 10:51:00 -0700 Subject: [PATCH 21/24] Fix tests --- test/graphql/queries.tests.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/graphql/queries.tests.js b/test/graphql/queries.tests.js index 87e4d0ac..7dbd0de2 100644 --- a/test/graphql/queries.tests.js +++ b/test/graphql/queries.tests.js @@ -453,7 +453,7 @@ describe('graphql/queries', () => { it('should return the query', async () => { const valid = ` query { - eventLeaderboard(filter: { + eventLeaderboardEntries(filter: { userAddress:"0x939592864c0bd3355b2d54e4fa2203e8343b6d6a", eventAddress:"0x09645ea6e4e1f5375f7596b73b3b597e6507201a" }) { @@ -467,7 +467,7 @@ describe('graphql/queries', () => { it('should accept the eventAddress filter', async () => { const valid = ` query { - eventLeaderboard(filter: { eventAddress: "0x09645ea6e4e1f5375f7596b73b3b597e6507201a" }) { + eventLeaderboardEntries(filter: { eventAddress: "0x09645ea6e4e1f5375f7596b73b3b597e6507201a" }) { ${PAGINATED_LEADERBOARD} } } @@ -478,7 +478,7 @@ describe('graphql/queries', () => { it('should accept the userAddress filter', async () => { const valid = ` query { - eventLeaderboard(filter: { userAddress:"0x939592864c0bd3355b2d54e4fa2203e8343b6d6a" }) { + eventLeaderboardEntries(filter: { userAddress:"0x939592864c0bd3355b2d54e4fa2203e8343b6d6a" }) { ${PAGINATED_LEADERBOARD} } } @@ -491,7 +491,7 @@ describe('graphql/queries', () => { it('should return the query', async () => { const valid = ` query { - globalLeaderboard(filter: { + globalLeaderboardEntries(filter: { userAddress:"0x939592864c0bd3355b2d54e4fa2203e8343b6d6a", eventAddress:"0x09645ea6e4e1f5375f7596b73b3b597e6507201a" }) { @@ -505,7 +505,7 @@ describe('graphql/queries', () => { it('should accept the eventAddress filter', async () => { const valid = ` query { - globalLeaderboard(filter: { eventAddress: "0x09645ea6e4e1f5375f7596b73b3b597e6507201a" }) { + globalLeaderboardEntries(filter: { eventAddress: "0x09645ea6e4e1f5375f7596b73b3b597e6507201a" }) { ${PAGINATED_LEADERBOARD} } } @@ -516,7 +516,7 @@ describe('graphql/queries', () => { it('should accept the userAddress filter', async () => { const valid = ` query { - globalLeaderboard(filter: { userAddress:"0x939592864c0bd3355b2d54e4fa2203e8343b6d6a" }) { + globalLeaderboardEntries(filter: { userAddress:"0x939592864c0bd3355b2d54e4fa2203e8343b6d6a" }) { ${PAGINATED_LEADERBOARD} } } From deeea38773189bd7cf7ce1df8dc034fefa513c11 Mon Sep 17 00:00:00 2001 From: Bernard Date: Thu, 11 Jul 2019 14:35:15 -0700 Subject: [PATCH 22/24] Changes requested --- src/graphql/queries/leaderboard.js | 9 ++++++--- src/sync/update-leaderboard.js | 8 ++------ test/db/index.js | 8 ++++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/graphql/queries/leaderboard.js b/src/graphql/queries/leaderboard.js index 72a8aaea..52ba1f99 100644 --- a/src/graphql/queries/leaderboard.js +++ b/src/graphql/queries/leaderboard.js @@ -1,4 +1,3 @@ -const { isArray, each } = require('lodash'); const BigNumber = require('bignumber.js'); const { lowercaseFilters, runPaginatedQuery } = require('./utils'); @@ -34,7 +33,9 @@ const eventLeaderboardEntries = async ( if (orderBy && orderBy.length > 0) { const { field } = orderBy[0]; - res.items.sort((a, b) => new BigNumber(b[field]).comparedTo(new BigNumber(a[field]))); // all descending + if (field !== 'eventAddress' && field !== 'userAddress') { + res.items.sort((a, b) => new BigNumber(b[field]).comparedTo(new BigNumber(a[field]))); // all descending + } } return res; @@ -56,7 +57,9 @@ const globalLeaderboardEntries = async ( if (orderBy && orderBy.length > 0) { const { field } = orderBy[0]; - res.items.sort((a, b) => new BigNumber(b[field]).comparedTo(new BigNumber(a[field]))); // all descending + if (field !== 'eventAddress' && field !== 'userAddress') { + res.items.sort((a, b) => new BigNumber(b[field]).comparedTo(new BigNumber(a[field]))); // all descending + } } return res; diff --git a/src/sync/update-leaderboard.js b/src/sync/update-leaderboard.js index b13abccf..9d1818e7 100644 --- a/src/sync/update-leaderboard.js +++ b/src/sync/update-leaderboard.js @@ -1,13 +1,9 @@ -const { each, isNull, find } = require('lodash'); +const { each, find } = require('lodash'); const async = require('async'); const web3 = require('../web3'); const { TX_STATUS } = require('../constants'); -const EventSig = require('../config/event-sig'); -const { toLowerCase } = require('../utils'); -const { getTransactionReceipt } = require('../utils/web3-utils'); const logger = require('../utils/logger'); const DBHelper = require('../db/db-helper'); -const parseEvent = require('./parsers/multiple-results-event'); const MultipleResultsEventApi = require('../api/multiple-results-event'); const EventLeaderboard = require('../models/event-leaderboard'); const GlobalLeaderboard = require('../models/global-leaderboard'); @@ -88,7 +84,7 @@ const updateLeaderboardWithdrawing = async (syncPromises, events, limit) => { filtered.push(tx); } }); - async.eachOfSeries(filtered, (value, key, callback) => { + await async.eachOfSeries(filtered, (value, key, callback) => { try { const { eventAddress, betterAddress, centralizedOracleAddress } = value; const userAddress = betterAddress || centralizedOracleAddress; diff --git a/test/db/index.js b/test/db/index.js index 8d9ed2c7..2869512f 100644 --- a/test/db/index.js +++ b/test/db/index.js @@ -1800,7 +1800,7 @@ describe('db', () => { let eventLeaderboardEntryCount; describe('findEventLeaderboard', () => { - it('find empty withdraw db', async () => { + it('find empty leaderboard db', async () => { eventLeaderboardEntryCount = await db.EventLeaderboard.count({}); eventLeaderboardEntryCount.should.equal(0); eventLeaderboardEntry = await DBHelper.findEventLeaderboard({}); @@ -1943,7 +1943,7 @@ describe('db', () => { expect(eventLeaderboardEntry).excluding(['_id']).to.deep.equal(newEntry); }); - it('should not update withdraw when eventAddress is not existed', async () => { + it('should not update leaderboard when eventAddress is not existed', async () => { const newEntry = { eventAddress: '0x12345', userAddress: '0x123456764c0bd3355b2d54e4fa2203e8343b6d6a', @@ -1963,7 +1963,7 @@ describe('db', () => { should.not.exist(eventLeaderboardEntry); }); - it('should not update withdraw when userAddress is not existed', async () => { + it('should not update leaderboard when userAddress is not existed', async () => { const newEntry = { eventAddress: '0x12345ea6e4e1f5375f7596b73b3b597e6507201a', userAddress: '0x12345', @@ -2131,7 +2131,7 @@ describe('db', () => { expect(globalLeaderboardEntry).excluding(['_id']).to.deep.equal(newEntry); }); - it('should not update withdraw when userAddress is not existed', async () => { + it('should not update leaderboard when userAddress is not existed', async () => { const newEntry = { userAddress: '0x12345', investments: '0', From 08e6b99aa84ff0d461fec6b7f1a40ec88e0a2a11 Mon Sep 17 00:00:00 2001 From: Bowen Zhang Date: Thu, 11 Jul 2019 14:51:41 -0700 Subject: [PATCH 23/24] Fix #274 migration not stop on error --- src/db/migrations/migration2.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/db/migrations/migration2.js b/src/db/migrations/migration2.js index 243a82a7..6c736ae8 100644 --- a/src/db/migrations/migration2.js +++ b/src/db/migrations/migration2.js @@ -1,5 +1,5 @@ const pLimit = require('p-limit'); -const async = require('async'); +const { whilst, eachOfSeries } = require('async'); const logger = require('../../utils/logger'); const DBHelper = require('../db-helper'); const { TX_STATUS, EVENT_STATUS } = require('../../constants'); @@ -28,7 +28,7 @@ async function migration2(next) { try { let i = 0; - await async.whilst( + await whilst( check => check(null, i < txs.length), // trigger iter async () => { const tx = txs[i]; @@ -64,7 +64,7 @@ async function migration2(next) { if (tx.betterAddress) addresses.add(tx.betterAddress); if (tx.centralizedOracleAddress) addresses.add(tx.centralizedOracleAddress); }); - async.eachOfSeries(addresses, async (address, index) => { + await eachOfSeries(addresses, async (address, index) => { try { // calculate winning for this user in this event const res = await MultipleResultsEventApi.calculateWinnings({ @@ -88,6 +88,7 @@ async function migration2(next) { await DBHelper.insertGlobalLeaderboard(globalLeaderboardEntry); } catch (err) { logger.error('Migrate event leaderboard error:', err); + throw err; } }); } From f3b885f81153a66a17035606f36e283114198d69 Mon Sep 17 00:00:00 2001 From: Deric Walintukan Date: Fri, 12 Jul 2019 10:28:38 +0700 Subject: [PATCH 24/24] Update version --- package-lock.json | 43 ++++++++++++------------------------------- package.json | 2 +- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7cacdda2..23fdba56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bodhi-server", - "version": "6.1.3", + "version": "6.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3228,8 +3228,7 @@ "version": "2.1.1", "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -3253,15 +3252,13 @@ "version": "1.0.0", "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3278,22 +3275,19 @@ "version": "1.1.0", "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -3424,8 +3418,7 @@ "version": "2.0.3", "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -3439,7 +3432,6 @@ "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3456,7 +3448,6 @@ "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3465,15 +3456,13 @@ "version": "0.0.8", "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "resolved": false, "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -3494,7 +3483,6 @@ "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -3583,8 +3571,7 @@ "version": "1.0.1", "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3598,7 +3585,6 @@ "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3694,8 +3680,7 @@ "version": "5.1.1", "resolved": false, "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -3737,7 +3722,6 @@ "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3759,7 +3743,6 @@ "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3808,15 +3791,13 @@ "version": "1.0.2", "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "resolved": false, "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", - "dev": true, - "optional": true + "dev": true } } }, diff --git a/package.json b/package.json index fe8d4b36..ea9e57a1 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "Server that parses, stores, sends, and receives Bodhi-related data from the blockchain.", "author": "bodhi.network", "license": "LGPL-3.0", - "version": "6.1.3", + "version": "6.2.0", "repository": "git@github.com:bodhiproject/bodhi-server.git", "keywords": [ "bodhi",