From 91970fbb257da0e6b3dc22fa9bc5925dedfecfcf Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Thu, 20 Feb 2020 13:39:28 -0500 Subject: [PATCH 01/12] fix commit/reveal for metamask Signed-off-by: Nick Pai --- common/MetaMaskTruffleProvider.js | 2 ++ common/VotingUtils.js | 24 +++++++++++++++++++----- common/globalTruffleConfig.js | 4 ++-- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/common/MetaMaskTruffleProvider.js b/common/MetaMaskTruffleProvider.js index 20e51a832a..839d02ef83 100644 --- a/common/MetaMaskTruffleProvider.js +++ b/common/MetaMaskTruffleProvider.js @@ -7,6 +7,8 @@ class MetaMaskTruffleProvider { constructor() { this.wrappedProvider = null; this.wrappedProviderPromise = this.getOrConstructWrappedProvider(); + // Adding this "label" property is a hacky solution so that truffle can detect that we are using a Metamask provider. + this.label = "metamask" } // Passes the call through, by attaching a callback to the wrapper provider promise. diff --git a/common/VotingUtils.js b/common/VotingUtils.js index 0e1e35b305..7159a30710 100644 --- a/common/VotingUtils.js +++ b/common/VotingUtils.js @@ -1,4 +1,4 @@ -const { decryptMessage, encryptMessage, deriveKeyPairFromSignatureTruffle } = require("./Crypto"); +const { decryptMessage, encryptMessage, deriveKeyPairFromSignatureTruffle, deriveKeyPairFromSignatureMetamask } = require("./Crypto"); const { getKeyGenMessage, computeTopicHash } = require("./EncryptionHelper"); const { BATCH_MAX_COMMITS, BATCH_MAX_RETRIEVALS, BATCH_MAX_REVEALS } = require("./Constants"); @@ -17,8 +17,15 @@ const constructCommitment = async (request, roundId, web3, price, account) => { const hash = web3.utils.soliditySha3(priceWei, salt); const vote = { price: priceWei, salt }; - const { publicKey } = await deriveKeyPairFromSignatureTruffle(web3, getKeyGenMessage(roundId), account); - const encryptedVote = await encryptMessage(publicKey, JSON.stringify(vote)); + let derivedPublicKey; + if (web3.currentProvider.label === "metamask") { + const { publicKey } = await deriveKeyPairFromSignatureMetamask(web3, getKeyGenMessage(roundId), account); + derivedPublicKey = publicKey + } else { + const { publicKey } = await deriveKeyPairFromSignatureTruffle(web3, getKeyGenMessage(roundId), account); + derivedPublicKey = publicKey + } + const encryptedVote = await encryptMessage(derivedPublicKey, JSON.stringify(vote)); return { identifier: request.identifier, @@ -42,8 +49,15 @@ const constructReveal = async (request, roundId, web3, account, votingContract) const topicHash = computeTopicHash(request, roundId); const encryptedCommit = await votingContract.getMessage(account, topicHash, { from: account }); - const { privateKey } = await deriveKeyPairFromSignatureTruffle(web3, getKeyGenMessage(roundId), account); - const vote = JSON.parse(await decryptMessage(privateKey, encryptedCommit)); + let derivedPrivateKey; + if (web3.currentProvider.label === "metamask") { + const { privateKey } = await deriveKeyPairFromSignatureMetamask(web3, getKeyGenMessage(roundId), account); + derivedPrivateKey = privateKey + } else { + const { privateKey } = await deriveKeyPairFromSignatureTruffle(web3, getKeyGenMessage(roundId), account); + derivedPrivateKey = privateKey + } + const vote = JSON.parse(await decryptMessage(derivedPrivateKey, encryptedCommit)); return { identifier: request.identifier, diff --git a/common/globalTruffleConfig.js b/common/globalTruffleConfig.js index ed7c463757..7817e56b39 100644 --- a/common/globalTruffleConfig.js +++ b/common/globalTruffleConfig.js @@ -123,8 +123,8 @@ addLocalNetwork(networks, "test"); // Coverage requires specific parameters to allow very high cost transactions. addLocalNetwork(networks, "coverage", { port: 8545, network_id: 1234 }); -// MetaMask truffle provider -addLocalNetwork(networks, "metamask", { provider: new MetaMaskTruffleProvider() }); +// MetaMask truffle provider requires a longer timeout so that user has time to point web browser with metamask to localhost:3333 +addLocalNetwork(networks, "metamask", { provider: new MetaMaskTruffleProvider(), networkCheckTimeout: 500000 }); module.exports = { // See From 2b8219cc937738736d596d7eab28a2c31ade7e8f Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Thu, 20 Feb 2020 16:27:04 -0500 Subject: [PATCH 02/12] remove any actions that call web3.contract.getPastEvents from metamask provider Signed-off-by: Nick Pai --- core/scripts/cli/voting/displayStatus.js | 67 ++++++++++++---------- core/scripts/cli/voting/retrieveRewards.js | 11 +++- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/core/scripts/cli/voting/displayStatus.js b/core/scripts/cli/voting/displayStatus.js index 0454b909fe..27afee58c5 100644 --- a/core/scripts/cli/voting/displayStatus.js +++ b/core/scripts/cli/voting/displayStatus.js @@ -31,8 +31,13 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { // If the user is using the two key contract, then the account is the designated voting contract's address const account = designatedVoting ? designatedVoting.address : await getDefaultAccount(web3); const filteredRequests = await filterRequests(pendingRequests, account, roundId, roundPhase, voting); - const rewards = await getAvailableRewards(web3, voting, account); - const resolvedPrices = await getResolvedPrices(web3, voting, account); + // TODO(#901): MetaMask provider sometimes has trouble reading past events + let rewards; + let resolvedPrices; + if (web3.currentProvider.label !== 'metamask') { + rewards = await getAvailableRewards(web3, voting, account); + resolvedPrices = await getResolvedPrices(web3, voting, account); + } style.spinnerReadingContracts.stop(); // TODO: #901 Can't access Voting.rounds in latest deployed Contract https://etherscan.io/address/0xfe3c4f1ec9f5df918d42ef7ed3fba81cc0086c5f#readContract @@ -87,36 +92,40 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { } // Display rewards to be retrieved in a table - console.log(`${style.success(`- Voting Rewards Available`)}:`); - if (rewards.roundIds.length > 0) { - const reducer = (accumulator, currentValue) => accumulator.concat(currentValue); - const rewardsTable = Object.values(rewards.rewardsByRoundId) - .reduce(reducer) - .map(reward => { - return { - round_id: reward.roundId, - name: reward.name, - reward_tokens: web3.utils.fromWei(reward.potentialRewards) - }; - }); - console.table(rewardsTable); + if (rewards) { + console.log(`${style.success(`- Voting Rewards Available`)}:`); + if (rewards.roundIds.length > 0) { + const reducer = (accumulator, currentValue) => accumulator.concat(currentValue); + const rewardsTable = Object.values(rewards.rewardsByRoundId) + .reduce(reducer) + .map(reward => { + return { + round_id: reward.roundId, + name: reward.name, + reward_tokens: web3.utils.fromWei(reward.potentialRewards) + }; + }); + console.table(rewardsTable); + } } // Display resolved prices that voter voted on - console.log(`${style.success(`- Resolved Prices of Votes Participated In`)}:`); - if (Object.keys(resolvedPrices).length > 0) { - const reducer = (accumulator, currentValue) => accumulator.concat(currentValue); - const resolvedPricesTable = Object.values(resolvedPrices) - .reduce(reducer) - .map(resolution => { - return { - round_id: resolution.roundId, - identifier: resolution.identifier, - time: resolution.time, - price: resolution.price - }; - }); - console.table(resolvedPricesTable); + if (resolvedPrices) { + console.log(`${style.success(`- Resolved Prices of Votes Participated In`)}:`); + if (Object.keys(resolvedPrices).length > 0) { + const reducer = (accumulator, currentValue) => accumulator.concat(currentValue); + const resolvedPricesTable = Object.values(resolvedPrices) + .reduce(reducer) + .map(resolution => { + return { + round_id: resolution.roundId, + identifier: resolution.identifier, + time: resolution.time, + price: resolution.price + }; + }); + console.table(resolvedPricesTable); + } } console.log(`\n`); diff --git a/core/scripts/cli/voting/retrieveRewards.js b/core/scripts/cli/voting/retrieveRewards.js index f48fc69454..7dadcb8056 100644 --- a/core/scripts/cli/voting/retrieveRewards.js +++ b/core/scripts/cli/voting/retrieveRewards.js @@ -12,6 +12,13 @@ const inquirer = require("inquirer"); * @param {* Object} voting deployed Voting.sol contract instance */ const retrieveRewards = async (web3, voting, designatedVoting) => { + + // TODO(#901): MetaMask provider sometimes has trouble reading past events, making retrieving rewards troublesome + if (web3.currentProvider.label === 'metamask') { + console.log(`Sorry! Retrieving rewards is not yet supported for Metamask providers.`); + return; + } + style.spinnerReadingContracts.start(); // If the user is using the two key contract, then the account is the designated voting contract's address const account = designatedVoting ? designatedVoting.address : await getDefaultAccount(web3); @@ -21,7 +28,7 @@ const retrieveRewards = async (web3, voting, designatedVoting) => { if (roundIds.length > 0) { console.group( `${style.instruction( - `\nPlease select which round ID of resolved price requests you would like to retrieve rewards for` + `\nPlease select which round ID of resolved price requests you would like to retrieve rewards for:` )}` ); roundIds.push({ name: "back" }); @@ -57,7 +64,7 @@ const retrieveRewards = async (web3, voting, designatedVoting) => { console.log(`\n`); console.groupEnd(); } else { - console.log(`You have no rewards to retrieve`); + console.log(`You have no rewards to retrieve.`); } }; From 88a6a2683000a8a9ebd74d901bf368f3713bf7ca Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Thu, 20 Feb 2020 16:27:29 -0500 Subject: [PATCH 03/12] prettier Signed-off-by: Nick Pai --- common/MetaMaskTruffleProvider.js | 2 +- common/VotingUtils.js | 15 ++++++++++----- common/globalTruffleConfig.js | 2 +- core/scripts/cli/voting/displayStatus.js | 6 +++--- core/scripts/cli/voting/retrieveRewards.js | 3 +-- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/common/MetaMaskTruffleProvider.js b/common/MetaMaskTruffleProvider.js index 839d02ef83..2aaa6f6736 100644 --- a/common/MetaMaskTruffleProvider.js +++ b/common/MetaMaskTruffleProvider.js @@ -8,7 +8,7 @@ class MetaMaskTruffleProvider { this.wrappedProvider = null; this.wrappedProviderPromise = this.getOrConstructWrappedProvider(); // Adding this "label" property is a hacky solution so that truffle can detect that we are using a Metamask provider. - this.label = "metamask" + this.label = "metamask"; } // Passes the call through, by attaching a callback to the wrapper provider promise. diff --git a/common/VotingUtils.js b/common/VotingUtils.js index 7159a30710..4a74a9a512 100644 --- a/common/VotingUtils.js +++ b/common/VotingUtils.js @@ -1,4 +1,9 @@ -const { decryptMessage, encryptMessage, deriveKeyPairFromSignatureTruffle, deriveKeyPairFromSignatureMetamask } = require("./Crypto"); +const { + decryptMessage, + encryptMessage, + deriveKeyPairFromSignatureTruffle, + deriveKeyPairFromSignatureMetamask +} = require("./Crypto"); const { getKeyGenMessage, computeTopicHash } = require("./EncryptionHelper"); const { BATCH_MAX_COMMITS, BATCH_MAX_RETRIEVALS, BATCH_MAX_REVEALS } = require("./Constants"); @@ -20,10 +25,10 @@ const constructCommitment = async (request, roundId, web3, price, account) => { let derivedPublicKey; if (web3.currentProvider.label === "metamask") { const { publicKey } = await deriveKeyPairFromSignatureMetamask(web3, getKeyGenMessage(roundId), account); - derivedPublicKey = publicKey + derivedPublicKey = publicKey; } else { const { publicKey } = await deriveKeyPairFromSignatureTruffle(web3, getKeyGenMessage(roundId), account); - derivedPublicKey = publicKey + derivedPublicKey = publicKey; } const encryptedVote = await encryptMessage(derivedPublicKey, JSON.stringify(vote)); @@ -52,10 +57,10 @@ const constructReveal = async (request, roundId, web3, account, votingContract) let derivedPrivateKey; if (web3.currentProvider.label === "metamask") { const { privateKey } = await deriveKeyPairFromSignatureMetamask(web3, getKeyGenMessage(roundId), account); - derivedPrivateKey = privateKey + derivedPrivateKey = privateKey; } else { const { privateKey } = await deriveKeyPairFromSignatureTruffle(web3, getKeyGenMessage(roundId), account); - derivedPrivateKey = privateKey + derivedPrivateKey = privateKey; } const vote = JSON.parse(await decryptMessage(derivedPrivateKey, encryptedCommit)); diff --git a/common/globalTruffleConfig.js b/common/globalTruffleConfig.js index 7817e56b39..d42022b228 100644 --- a/common/globalTruffleConfig.js +++ b/common/globalTruffleConfig.js @@ -124,7 +124,7 @@ addLocalNetwork(networks, "test"); addLocalNetwork(networks, "coverage", { port: 8545, network_id: 1234 }); // MetaMask truffle provider requires a longer timeout so that user has time to point web browser with metamask to localhost:3333 -addLocalNetwork(networks, "metamask", { provider: new MetaMaskTruffleProvider(), networkCheckTimeout: 500000 }); +addLocalNetwork(networks, "metamask", { provider: new MetaMaskTruffleProvider(), networkCheckTimeout: 500000 }); module.exports = { // See diff --git a/core/scripts/cli/voting/displayStatus.js b/core/scripts/cli/voting/displayStatus.js index 27afee58c5..00b38693b6 100644 --- a/core/scripts/cli/voting/displayStatus.js +++ b/core/scripts/cli/voting/displayStatus.js @@ -34,7 +34,7 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { // TODO(#901): MetaMask provider sometimes has trouble reading past events let rewards; let resolvedPrices; - if (web3.currentProvider.label !== 'metamask') { + if (web3.currentProvider.label !== "metamask") { rewards = await getAvailableRewards(web3, voting, account); resolvedPrices = await getResolvedPrices(web3, voting, account); } @@ -106,7 +106,7 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { }; }); console.table(rewardsTable); - } + } } // Display resolved prices that voter voted on @@ -125,7 +125,7 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { }; }); console.table(resolvedPricesTable); - } + } } console.log(`\n`); diff --git a/core/scripts/cli/voting/retrieveRewards.js b/core/scripts/cli/voting/retrieveRewards.js index 7dadcb8056..918b3e7941 100644 --- a/core/scripts/cli/voting/retrieveRewards.js +++ b/core/scripts/cli/voting/retrieveRewards.js @@ -12,9 +12,8 @@ const inquirer = require("inquirer"); * @param {* Object} voting deployed Voting.sol contract instance */ const retrieveRewards = async (web3, voting, designatedVoting) => { - // TODO(#901): MetaMask provider sometimes has trouble reading past events, making retrieving rewards troublesome - if (web3.currentProvider.label === 'metamask') { + if (web3.currentProvider.label === "metamask") { console.log(`Sorry! Retrieving rewards is not yet supported for Metamask providers.`); return; } From 4badc68d78c74cb7be9a6eac0532d402dd553d70 Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Thu, 20 Feb 2020 18:07:10 -0500 Subject: [PATCH 04/12] use forked version of node-metamask Signed-off-by: Nick Pai --- core/scripts/cli/voting/displayStatus.js | 32 ++++++++++------------ core/scripts/cli/voting/retrieveRewards.js | 6 ---- package.json | 2 +- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/core/scripts/cli/voting/displayStatus.js b/core/scripts/cli/voting/displayStatus.js index 00b38693b6..29b7c0c96c 100644 --- a/core/scripts/cli/voting/displayStatus.js +++ b/core/scripts/cli/voting/displayStatus.js @@ -26,16 +26,14 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { const roundPhase = (await voting.getVotePhase()).toString(); // TODO: #901 Can't access Voting.rounds in latest deployed Contract https://etherscan.io/address/0xfe3c4f1ec9f5df918d42ef7ed3fba81cc0086c5f#readContract // const roundStats = await voting.rounds(roundId); - const currentTime = await voting.getCurrentTime(); // If the user is using the two key contract, then the account is the designated voting contract's address const account = designatedVoting ? designatedVoting.address : await getDefaultAccount(web3); const filteredRequests = await filterRequests(pendingRequests, account, roundId, roundPhase, voting); + let rewards = await getAvailableRewards(web3, voting, account); // TODO(#901): MetaMask provider sometimes has trouble reading past events - let rewards; let resolvedPrices; if (web3.currentProvider.label !== "metamask") { - rewards = await getAvailableRewards(web3, voting, account); resolvedPrices = await getResolvedPrices(web3, voting, account); } style.spinnerReadingContracts.stop(); @@ -92,21 +90,19 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { } // Display rewards to be retrieved in a table - if (rewards) { - console.log(`${style.success(`- Voting Rewards Available`)}:`); - if (rewards.roundIds.length > 0) { - const reducer = (accumulator, currentValue) => accumulator.concat(currentValue); - const rewardsTable = Object.values(rewards.rewardsByRoundId) - .reduce(reducer) - .map(reward => { - return { - round_id: reward.roundId, - name: reward.name, - reward_tokens: web3.utils.fromWei(reward.potentialRewards) - }; - }); - console.table(rewardsTable); - } + console.log(`${style.success(`- Voting Rewards Available`)}:`); + if (rewards.roundIds.length > 0) { + const reducer = (accumulator, currentValue) => accumulator.concat(currentValue); + const rewardsTable = Object.values(rewards.rewardsByRoundId) + .reduce(reducer) + .map(reward => { + return { + round_id: reward.roundId, + name: reward.name, + reward_tokens: web3.utils.fromWei(reward.potentialRewards) + }; + }); + console.table(rewardsTable); } // Display resolved prices that voter voted on diff --git a/core/scripts/cli/voting/retrieveRewards.js b/core/scripts/cli/voting/retrieveRewards.js index 918b3e7941..402b040681 100644 --- a/core/scripts/cli/voting/retrieveRewards.js +++ b/core/scripts/cli/voting/retrieveRewards.js @@ -12,12 +12,6 @@ const inquirer = require("inquirer"); * @param {* Object} voting deployed Voting.sol contract instance */ const retrieveRewards = async (web3, voting, designatedVoting) => { - // TODO(#901): MetaMask provider sometimes has trouble reading past events, making retrieving rewards troublesome - if (web3.currentProvider.label === "metamask") { - console.log(`Sorry! Retrieving rewards is not yet supported for Metamask providers.`); - return; - } - style.spinnerReadingContracts.start(); // If the user is using the two key contract, then the account is the designated voting contract's address const account = designatedVoting ? designatedVoting.address : await getDefaultAccount(web3); diff --git a/package.json b/package.json index c7914ede9a..e72e873c1c 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "minimist": "^1.2.0", "moment": "^2.24.0", "node-fetch": "^2.3.0", - "node-metamask": "^1.1.2", + "node-metamask": "github:UMAprotocol/node-metamask", "ora": "^4.0.3", "require-context": "^1.1.0", "secp256k1": "^3.7.1", From 22f2f348387957011133a6392a684f2e49cb2faf Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Thu, 20 Feb 2020 18:43:09 -0500 Subject: [PATCH 05/12] add messaging that metamask provider might not read events properly Signed-off-by: Nick Pai --- core/scripts/cli/vote.js | 6 +++++- core/scripts/cli/voting/retrieveRewards.js | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/core/scripts/cli/vote.js b/core/scripts/cli/vote.js index d66780f4f1..179e60d84d 100644 --- a/core/scripts/cli/vote.js +++ b/core/scripts/cli/vote.js @@ -77,7 +77,11 @@ const votingMenu = async function(web3, artifacts) { ACTIONS.reveal )}: Prompts user to select batch of votes to reveal. Only possible during the Reveal phase.` ); - console.log(`${style.help(ACTIONS.rewards)}: Prompts user to select resolved votes to retrieve rewards for.`); + console.log( + `${style.help( + ACTIONS.rewards + )}: Prompts user to select resolved votes to retrieve rewards for. This might not work perfectly if you are using a Metamask provider.` + ); console.groupEnd(); break; diff --git a/core/scripts/cli/voting/retrieveRewards.js b/core/scripts/cli/voting/retrieveRewards.js index 402b040681..d01ad452a6 100644 --- a/core/scripts/cli/voting/retrieveRewards.js +++ b/core/scripts/cli/voting/retrieveRewards.js @@ -57,7 +57,9 @@ const retrieveRewards = async (web3, voting, designatedVoting) => { console.log(`\n`); console.groupEnd(); } else { - console.log(`You have no rewards to retrieve.`); + console.log( + `You have no rewards to retrieve. If you are using a MetaMask provider, then we might not be able to detect all of your rewards to be retrieved, sorry!` + ); } }; From 6f93626b513129d9cdabf087766757ae9f315bf8 Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Thu, 20 Feb 2020 18:52:48 -0500 Subject: [PATCH 06/12] one change: Signed-off-by: Nick Pai --- core/scripts/cli/voting/displayStatus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/scripts/cli/voting/displayStatus.js b/core/scripts/cli/voting/displayStatus.js index 29b7c0c96c..a9694cebf8 100644 --- a/core/scripts/cli/voting/displayStatus.js +++ b/core/scripts/cli/voting/displayStatus.js @@ -30,7 +30,7 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { // If the user is using the two key contract, then the account is the designated voting contract's address const account = designatedVoting ? designatedVoting.address : await getDefaultAccount(web3); const filteredRequests = await filterRequests(pendingRequests, account, roundId, roundPhase, voting); - let rewards = await getAvailableRewards(web3, voting, account); + const rewards = await getAvailableRewards(web3, voting, account); // TODO(#901): MetaMask provider sometimes has trouble reading past events let resolvedPrices; if (web3.currentProvider.label !== "metamask") { From 568ca4eb0bf5ee8be9749bd3dce9f9957a9e3883 Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Fri, 21 Feb 2020 13:14:03 -0500 Subject: [PATCH 07/12] remove rewards from voting-info Signed-off-by: Nick Pai --- core/scripts/cli/voting/displayStatus.js | 31 +++++++++++++----------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/core/scripts/cli/voting/displayStatus.js b/core/scripts/cli/voting/displayStatus.js index a9694cebf8..ab4af01c9a 100644 --- a/core/scripts/cli/voting/displayStatus.js +++ b/core/scripts/cli/voting/displayStatus.js @@ -30,11 +30,12 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { // If the user is using the two key contract, then the account is the designated voting contract's address const account = designatedVoting ? designatedVoting.address : await getDefaultAccount(web3); const filteredRequests = await filterRequests(pendingRequests, account, roundId, roundPhase, voting); - const rewards = await getAvailableRewards(web3, voting, account); // TODO(#901): MetaMask provider sometimes has trouble reading past events let resolvedPrices; + let rewards; if (web3.currentProvider.label !== "metamask") { resolvedPrices = await getResolvedPrices(web3, voting, account); + rewards = await getAvailableRewards(web3, voting, account); } style.spinnerReadingContracts.stop(); @@ -90,19 +91,21 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { } // Display rewards to be retrieved in a table - console.log(`${style.success(`- Voting Rewards Available`)}:`); - if (rewards.roundIds.length > 0) { - const reducer = (accumulator, currentValue) => accumulator.concat(currentValue); - const rewardsTable = Object.values(rewards.rewardsByRoundId) - .reduce(reducer) - .map(reward => { - return { - round_id: reward.roundId, - name: reward.name, - reward_tokens: web3.utils.fromWei(reward.potentialRewards) - }; - }); - console.table(rewardsTable); + if (rewards) { + console.log(`${style.success(`- Voting Rewards Available`)}:`); + if (rewards.roundIds.length > 0) { + const reducer = (accumulator, currentValue) => accumulator.concat(currentValue); + const rewardsTable = Object.values(rewards.rewardsByRoundId) + .reduce(reducer) + .map(reward => { + return { + round_id: reward.roundId, + name: reward.name, + reward_tokens: web3.utils.fromWei(reward.potentialRewards) + }; + }); + console.table(rewardsTable); + } } // Display resolved prices that voter voted on From 1af58f4220201e4653d9161e32ff7856a6c69adc Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Fri, 21 Feb 2020 13:17:24 -0500 Subject: [PATCH 08/12] more messaging Signed-off-by: Nick Pai --- core/scripts/cli/voting/displayStatus.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/scripts/cli/voting/displayStatus.js b/core/scripts/cli/voting/displayStatus.js index ab4af01c9a..4e9054e1f4 100644 --- a/core/scripts/cli/voting/displayStatus.js +++ b/core/scripts/cli/voting/displayStatus.js @@ -106,6 +106,8 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { }); console.table(rewardsTable); } + } else { + console.log(`- Cannot display available voting rewards for Metamask users`); } // Display resolved prices that voter voted on @@ -125,6 +127,8 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { }); console.table(resolvedPricesTable); } + } else { + console.log(`- Cannot display past vote results for Metamask users`); } console.log(`\n`); From 0b15f629f2e4362243123577b892e301cbd35319 Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Fri, 21 Feb 2020 13:19:52 -0500 Subject: [PATCH 09/12] colors Signed-off-by: Nick Pai --- core/scripts/cli/textStyle.js | 1 + core/scripts/cli/voting/displayStatus.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/scripts/cli/textStyle.js b/core/scripts/cli/textStyle.js index 77f44a3057..a7b6d95ada 100644 --- a/core/scripts/cli/textStyle.js +++ b/core/scripts/cli/textStyle.js @@ -8,6 +8,7 @@ const style = { // Colors instruction: chalkPipe("bgRed"), success: chalkPipe("bgGreen"), + warning: chalkPipe("bgYellow"), help: chalkPipe("bgCyan"), // Links diff --git a/core/scripts/cli/voting/displayStatus.js b/core/scripts/cli/voting/displayStatus.js index 4e9054e1f4..d255731c68 100644 --- a/core/scripts/cli/voting/displayStatus.js +++ b/core/scripts/cli/voting/displayStatus.js @@ -107,7 +107,7 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { console.table(rewardsTable); } } else { - console.log(`- Cannot display available voting rewards for Metamask users`); + console.log(`${style.warning(`- Cannot display available voting rewards for Metamask users`)}:`); } // Display resolved prices that voter voted on @@ -128,7 +128,7 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { console.table(resolvedPricesTable); } } else { - console.log(`- Cannot display past vote results for Metamask users`); + console.log(`${style.warning(`- Cannot display past vote results for Metamask users`)}:`); } console.log(`\n`); From 6a5c157b1d08f6ebd58b320487873fea526313e0 Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Fri, 21 Feb 2020 13:35:37 -0500 Subject: [PATCH 10/12] move check for metamask closer to problem Signed-off-by: Nick Pai --- common/MetaMaskTruffleProvider.js | 2 -- common/VotingUtils.js | 6 ++++-- core/scripts/cli/voting/displayStatus.js | 13 ++++--------- .../scripts/cli/voting/getResolvedVotesByRoundId.js | 6 ++++++ core/scripts/cli/voting/getRewardsByRoundId.js | 6 ++++++ core/scripts/cli/voting/retrieveRewards.js | 13 ++++++++++--- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/common/MetaMaskTruffleProvider.js b/common/MetaMaskTruffleProvider.js index 2aaa6f6736..20e51a832a 100644 --- a/common/MetaMaskTruffleProvider.js +++ b/common/MetaMaskTruffleProvider.js @@ -7,8 +7,6 @@ class MetaMaskTruffleProvider { constructor() { this.wrappedProvider = null; this.wrappedProviderPromise = this.getOrConstructWrappedProvider(); - // Adding this "label" property is a hacky solution so that truffle can detect that we are using a Metamask provider. - this.label = "metamask"; } // Passes the call through, by attaching a callback to the wrapper provider promise. diff --git a/common/VotingUtils.js b/common/VotingUtils.js index 4a74a9a512..4c879cad75 100644 --- a/common/VotingUtils.js +++ b/common/VotingUtils.js @@ -7,6 +7,8 @@ const { const { getKeyGenMessage, computeTopicHash } = require("./EncryptionHelper"); const { BATCH_MAX_COMMITS, BATCH_MAX_RETRIEVALS, BATCH_MAX_REVEALS } = require("./Constants"); +const argv = require("minimist")(process.argv.slice()); + /** * Generate a salt and use it to encrypt a committed vote in response to a price request * Return committed vote details to the voter @@ -23,7 +25,7 @@ const constructCommitment = async (request, roundId, web3, price, account) => { const vote = { price: priceWei, salt }; let derivedPublicKey; - if (web3.currentProvider.label === "metamask") { + if (argv.network === "metamask") { const { publicKey } = await deriveKeyPairFromSignatureMetamask(web3, getKeyGenMessage(roundId), account); derivedPublicKey = publicKey; } else { @@ -55,7 +57,7 @@ const constructReveal = async (request, roundId, web3, account, votingContract) const encryptedCommit = await votingContract.getMessage(account, topicHash, { from: account }); let derivedPrivateKey; - if (web3.currentProvider.label === "metamask") { + if (argv.network === "metamask") { const { privateKey } = await deriveKeyPairFromSignatureMetamask(web3, getKeyGenMessage(roundId), account); derivedPrivateKey = privateKey; } else { diff --git a/core/scripts/cli/voting/displayStatus.js b/core/scripts/cli/voting/displayStatus.js index d255731c68..2e51a02a9f 100644 --- a/core/scripts/cli/voting/displayStatus.js +++ b/core/scripts/cli/voting/displayStatus.js @@ -30,13 +30,8 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { // If the user is using the two key contract, then the account is the designated voting contract's address const account = designatedVoting ? designatedVoting.address : await getDefaultAccount(web3); const filteredRequests = await filterRequests(pendingRequests, account, roundId, roundPhase, voting); - // TODO(#901): MetaMask provider sometimes has trouble reading past events - let resolvedPrices; - let rewards; - if (web3.currentProvider.label !== "metamask") { - resolvedPrices = await getResolvedPrices(web3, voting, account); - rewards = await getAvailableRewards(web3, voting, account); - } + const resolvedPrices = await getResolvedPrices(web3, voting, account); + const rewards = await getAvailableRewards(web3, voting, account); style.spinnerReadingContracts.stop(); // TODO: #901 Can't access Voting.rounds in latest deployed Contract https://etherscan.io/address/0xfe3c4f1ec9f5df918d42ef7ed3fba81cc0086c5f#readContract @@ -107,7 +102,7 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { console.table(rewardsTable); } } else { - console.log(`${style.warning(`- Cannot display available voting rewards for Metamask users`)}:`); + console.log(`${style.warning(`- Cannot display available voting rewards for Metamask users`)}`); } // Display resolved prices that voter voted on @@ -128,7 +123,7 @@ const displayVoteStatus = async (web3, voting, designatedVoting) => { console.table(resolvedPricesTable); } } else { - console.log(`${style.warning(`- Cannot display past vote results for Metamask users`)}:`); + console.log(`${style.warning(`- Cannot display past vote results for Metamask users`)}`); } console.log(`\n`); diff --git a/core/scripts/cli/voting/getResolvedVotesByRoundId.js b/core/scripts/cli/voting/getResolvedVotesByRoundId.js index ba887f944e..9717b6f6c4 100644 --- a/core/scripts/cli/voting/getResolvedVotesByRoundId.js +++ b/core/scripts/cli/voting/getResolvedVotesByRoundId.js @@ -1,4 +1,5 @@ const style = require("../textStyle"); +const argv = require("minimist")(process.argv.slice()); /** * Return the list of votes (that the voter has participated in) that have successfully resolved a price @@ -9,6 +10,11 @@ const style = require("../textStyle"); * @param {* String} account Etheruem account of voter */ const getResolvedVotesByRound = async (web3, votingContract, account) => { + // TODO(#901): MetaMask provider sometimes has trouble reading past events + if (argv.network === "metamask") { + return; + } + // First check list of votes revealed by user to determine which // price requests a user has voted on const revealedVotes = await votingContract.getPastEvents("VoteRevealed", { diff --git a/core/scripts/cli/voting/getRewardsByRoundId.js b/core/scripts/cli/voting/getRewardsByRoundId.js index b86eb5680a..c77e19f1b2 100644 --- a/core/scripts/cli/voting/getRewardsByRoundId.js +++ b/core/scripts/cli/voting/getRewardsByRoundId.js @@ -1,4 +1,5 @@ const style = require("../textStyle"); +const argv = require("minimist")(process.argv.slice()); /** * Given a list of revealed votes for a user, return @@ -18,6 +19,11 @@ const style = require("../textStyle"); * @param {* String} account Etheruem account of voter */ const getRewardsByRound = async (web3, votingContract, account) => { + // TODO(#901): MetaMask provider sometimes has trouble reading past events + if (argv.network === "metamask") { + return; + } + // All rewards available must be tied to formerly revealed votes const revealedVotes = await votingContract.getPastEvents("VoteRevealed", { filter: { voter: account }, diff --git a/core/scripts/cli/voting/retrieveRewards.js b/core/scripts/cli/voting/retrieveRewards.js index d01ad452a6..8e995e193e 100644 --- a/core/scripts/cli/voting/retrieveRewards.js +++ b/core/scripts/cli/voting/retrieveRewards.js @@ -3,6 +3,7 @@ const style = require("../textStyle"); const { batchRetrieveRewards } = require("../../../../common/VotingUtils"); const getAvailableRewards = require("./getRewardsByRoundId"); const inquirer = require("inquirer"); +const argv = require("minimist")(process.argv.slice()); /** * This prompts the user to select from a list of round ID's that have one or more rewards that can be retrieved. @@ -12,6 +13,14 @@ const inquirer = require("inquirer"); * @param {* Object} voting deployed Voting.sol contract instance */ const retrieveRewards = async (web3, voting, designatedVoting) => { + // TODO(#901): MetaMask provider sometimes has trouble reading past events + if (argv.network === "metamask") { + console.log( + `Sorry, we currently do not support retrieving rewards for Metamask users! Try again with another web3 provider.` + ); + return; + } + style.spinnerReadingContracts.start(); // If the user is using the two key contract, then the account is the designated voting contract's address const account = designatedVoting ? designatedVoting.address : await getDefaultAccount(web3); @@ -57,9 +66,7 @@ const retrieveRewards = async (web3, voting, designatedVoting) => { console.log(`\n`); console.groupEnd(); } else { - console.log( - `You have no rewards to retrieve. If you are using a MetaMask provider, then we might not be able to detect all of your rewards to be retrieved, sorry!` - ); + console.log(`You have no rewards to retrieve.`); } }; From 48cc0302f922f0f23a8507d7a0b3505c27859aec Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Fri, 21 Feb 2020 13:54:55 -0500 Subject: [PATCH 11/12] fix etherscan url Signed-off-by: Nick Pai --- core/scripts/cli/voting/commitVotes.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/core/scripts/cli/voting/commitVotes.js b/core/scripts/cli/voting/commitVotes.js index a1d6859512..1bc3faf828 100644 --- a/core/scripts/cli/voting/commitVotes.js +++ b/core/scripts/cli/voting/commitVotes.js @@ -4,6 +4,7 @@ const getDefaultAccount = require("../wallet/getDefaultAccount"); const filterRequests = require("./filterRequestsByRound"); const { VotePhasesEnum } = require("../../../../common/Enums"); const { constructCommitment, batchCommitVotes } = require("../../../../common/VotingUtils"); +const networkUtils = require("../../../../common/PublicNetworks"); /** * This prompts the user twice from the command line interface: @@ -79,6 +80,16 @@ const commitVotes = async (web3, voting, designatedVoting) => { const { successes, batches } = await batchCommitVotes(newCommitments, voting, account); style.spinnerWritingContracts.stop(); + // Construct etherscan link based on network + const networkId = web3.networkId; + let url; + if (networkUtils[networkId]) { + url = `${networkUtils[networkId].etherscan}/tx/`; + } else { + // No URL for localhost, just show transaction ID + url = ""; + } + // Print results console.log( style.success( @@ -89,7 +100,7 @@ const commitVotes = async (web3, voting, designatedVoting) => { ); console.group(style.success(`Receipts:`)); successes.forEach(committedVote => { - console.log(`- transaction: ${style.link(`https://etherscan.io/tx/${committedVote.txnHash}`)}`); + console.log(`- transaction: ${style.link(`${url}${committedVote.txnHash}`)}`); console.log(` - salt: ${committedVote.salt}`); console.log(` - voted price: ${web3.utils.fromWei(committedVote.price)}`); }); From 163ad025168acb331540cb87bf6e8c0273a715ec Mon Sep 17 00:00:00 2001 From: Nick Pai Date: Fri, 21 Feb 2020 16:09:28 -0500 Subject: [PATCH 12/12] refactor Signed-off-by: Nick Pai --- common/VotingUtils.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/common/VotingUtils.js b/common/VotingUtils.js index 4c879cad75..6ee433fb36 100644 --- a/common/VotingUtils.js +++ b/common/VotingUtils.js @@ -24,15 +24,13 @@ const constructCommitment = async (request, roundId, web3, price, account) => { const hash = web3.utils.soliditySha3(priceWei, salt); const vote = { price: priceWei, salt }; - let derivedPublicKey; + let publicKey; if (argv.network === "metamask") { - const { publicKey } = await deriveKeyPairFromSignatureMetamask(web3, getKeyGenMessage(roundId), account); - derivedPublicKey = publicKey; + publicKey = (await deriveKeyPairFromSignatureMetamask(web3, getKeyGenMessage(roundId), account)).publicKey; } else { - const { publicKey } = await deriveKeyPairFromSignatureTruffle(web3, getKeyGenMessage(roundId), account); - derivedPublicKey = publicKey; + publicKey = (await deriveKeyPairFromSignatureTruffle(web3, getKeyGenMessage(roundId), account)).publicKey; } - const encryptedVote = await encryptMessage(derivedPublicKey, JSON.stringify(vote)); + const encryptedVote = await encryptMessage(publicKey, JSON.stringify(vote)); return { identifier: request.identifier, @@ -56,15 +54,13 @@ const constructReveal = async (request, roundId, web3, account, votingContract) const topicHash = computeTopicHash(request, roundId); const encryptedCommit = await votingContract.getMessage(account, topicHash, { from: account }); - let derivedPrivateKey; + let privateKey; if (argv.network === "metamask") { - const { privateKey } = await deriveKeyPairFromSignatureMetamask(web3, getKeyGenMessage(roundId), account); - derivedPrivateKey = privateKey; + privateKey = (await deriveKeyPairFromSignatureMetamask(web3, getKeyGenMessage(roundId), account)).privateKey; } else { - const { privateKey } = await deriveKeyPairFromSignatureTruffle(web3, getKeyGenMessage(roundId), account); - derivedPrivateKey = privateKey; + privateKey = (await deriveKeyPairFromSignatureTruffle(web3, getKeyGenMessage(roundId), account)).privateKey; } - const vote = JSON.parse(await decryptMessage(derivedPrivateKey, encryptedCommit)); + const vote = JSON.parse(await decryptMessage(privateKey, encryptedCommit)); return { identifier: request.identifier,