From ca32bb6d5951d596a986f391bc969a29a51425c2 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Tue, 23 Jul 2019 12:58:16 -0700 Subject: [PATCH 01/41] Stub out rewards and rewardWeight bug in Bonded Deposits contract (#71) * Fix pre-alfajores bug in contracts * Fix solidity compiler errors * Fix e2e governance tests * Fix compiler errors in governance tests --- .../celotool/geth_tests/governance_tests.ts | 35 ++++++------ .../contracts/governance/BondedDeposits.sol | 56 ++++++++++--------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/packages/celotool/geth_tests/governance_tests.ts b/packages/celotool/geth_tests/governance_tests.ts index 8161e0fefef..9dad1adc62d 100644 --- a/packages/celotool/geth_tests/governance_tests.ts +++ b/packages/celotool/geth_tests/governance_tests.ts @@ -4,7 +4,6 @@ import { getHooks, importGenesis, initAndStartGeth, - sleep, } from '@celo/celotool/geth_tests/src/lib/utils' import BigNumber from 'bignumber.js' const assert = require('chai').assert @@ -141,17 +140,17 @@ describe('governance tests', () => { return await tx.send({ from: account, ...txOptions, gasPrice, gas }) } - const redeemRewards = async (account: string, txOptions: any = {}) => { - await unlockAccount(account, web3) - const tx = bondedDeposits.methods.redeemRewards() - let gas = txOptions.gas - // We overestimate to account for variations in the fraction reduction necessary to redeem - // rewards. - if (!gas) { - gas = 2 * (await tx.estimateGas({ ...txOptions })) - } - return await tx.send({ from: account, ...txOptions, gasPrice, gas }) - } + // const redeemRewards = async (account: string, txOptions: any = {}) => { + // await unlockAccount(account, web3) + // const tx = bondedDeposits.methods.redeemRewards() + // let gas = txOptions.gas + // // We overestimate to account for variations in the fraction reduction necessary to redeem + // // rewards. + // if (!gas) { + // gas = 2 * (await tx.estimateGas({ ...txOptions })) + // } + // return await tx.send({ from: account, ...txOptions, gasPrice, gas }) + // } describe('when a bonded deposit account with weight exists', () => { const account = '0x47e172f6cfb6c7d01c1574fa3e2be7cc73269d95' @@ -174,12 +173,12 @@ describe('governance tests', () => { await delegateRewards(account, delegate) }) - it('should be able to redeem block rewards', async function(this: any) { - this.timeout(0) // Disable test timeout - await sleep(1) - await redeemRewards(account) - assert.isAtLeast(await web3.eth.getBalance(delegate), 1) - }) + // it('should be able to redeem block rewards', async function(this: any) { + // this.timeout(0) // Disable test timeout + // await sleep(1) + // await redeemRewards(account) + // assert.isAtLeast(await web3.eth.getBalance(delegate), 1) + // }) }) describe('when adding any block', () => { diff --git a/packages/protocol/contracts/governance/BondedDeposits.sol b/packages/protocol/contracts/governance/BondedDeposits.sol index 607f8d6b4e0..c7016ccd746 100644 --- a/packages/protocol/contracts/governance/BondedDeposits.sol +++ b/packages/protocol/contracts/governance/BondedDeposits.sol @@ -180,30 +180,31 @@ contract BondedDeposits is IBondedDeposits, ReentrancyGuard, Initializable, Usin * @dev Called by the EVM at the end of the block. */ function setCumulativeRewardWeight(uint256 blockReward) external { + return; // TODO(asa): Modify ganache to set cumulativeRewardWeights. // TODO(asa): Make inheritable `onlyVm` modifier. // Only callable by the EVM. - require(msg.sender == address(0), "sender was not vm (reserved addr 0x0)"); - FractionUtil.Fraction storage previousCumulativeRewardWeight = cumulativeRewardWeights[ - block.number.sub(1) - ]; - - // This will be true the first time this is called by the EVM. - if (!previousCumulativeRewardWeight.exists()) { - previousCumulativeRewardWeight.denominator = 1; - } - - if (totalWeight > 0) { - FractionUtil.Fraction memory currentRewardWeight = FractionUtil.Fraction( - blockReward, - totalWeight - ).reduce(); - cumulativeRewardWeights[block.number] = previousCumulativeRewardWeight.add( - currentRewardWeight - ); - } else { - cumulativeRewardWeights[block.number] = previousCumulativeRewardWeight; - } + // require(msg.sender == address(0), "sender was not vm (reserved addr 0x0)"); + // FractionUtil.Fraction storage previousCumulativeRewardWeight = cumulativeRewardWeights[ + // block.number.sub(1) + // ]; + + // // This will be true the first time this is called by the EVM. + // if (!previousCumulativeRewardWeight.exists()) { + // previousCumulativeRewardWeight.denominator = 1; + // } + + // if (totalWeight > 0) { + // FractionUtil.Fraction memory currentRewardWeight = FractionUtil.Fraction( + // blockReward, + // totalWeight + // ).reduce(); + // cumulativeRewardWeights[block.number] = previousCumulativeRewardWeight.add( + // currentRewardWeight + // ); + // } else { + // cumulativeRewardWeights[block.number] = previousCumulativeRewardWeight; + // } } /** @@ -237,6 +238,7 @@ contract BondedDeposits is IBondedDeposits, ReentrancyGuard, Initializable, Usin * @dev Fails if `msg.sender` is not the owner or rewards recipient of the account. */ function redeemRewards() external nonReentrant returns (uint256) { + require(false, "Disabled"); address account = getAccountFromRewardsRecipient(msg.sender); return _redeemRewards(account); } @@ -350,7 +352,7 @@ contract BondedDeposits is IBondedDeposits, ReentrancyGuard, Initializable, Usin isNotAccount(delegate) isNotDelegate(delegate) { - _redeemRewards(msg.sender); + // _redeemRewards(msg.sender); // TODO(asa): Consider an additional prefix here. address signer = getSignerOfAddress(msg.sender, v, r, s); require(signer == delegate); @@ -376,7 +378,7 @@ contract BondedDeposits is IBondedDeposits, ReentrancyGuard, Initializable, Usin returns (uint256) { require(!isVoting(msg.sender)); - _redeemRewards(msg.sender); + // _redeemRewards(msg.sender); require(msg.value > 0 && noticePeriod <= maxNoticePeriod); Account storage account = accounts[msg.sender]; Deposit storage bonded = account.deposits.bonded[noticePeriod]; @@ -402,7 +404,7 @@ contract BondedDeposits is IBondedDeposits, ReentrancyGuard, Initializable, Usin returns (uint256) { require(!isVoting(msg.sender)); - _redeemRewards(msg.sender); + // _redeemRewards(msg.sender); Account storage account = accounts[msg.sender]; Deposit storage bonded = account.deposits.bonded[noticePeriod]; require(bonded.value >= value && value > 0); @@ -435,7 +437,7 @@ contract BondedDeposits is IBondedDeposits, ReentrancyGuard, Initializable, Usin // solhint-disable-next-line not-rely-on-time require(availabilityTime > now); require(!isVoting(msg.sender)); - _redeemRewards(msg.sender); + // _redeemRewards(msg.sender); Account storage account = accounts[msg.sender]; Deposit storage notified = account.deposits.notified[availabilityTime]; require(notified.value >= value && value > 0); @@ -462,7 +464,7 @@ contract BondedDeposits is IBondedDeposits, ReentrancyGuard, Initializable, Usin returns (uint256) { require(!isVoting(msg.sender)); - _redeemRewards(msg.sender); + // _redeemRewards(msg.sender); // solhint-disable-next-line not-rely-on-time require(now >= availabilityTime); Account storage account = accounts[msg.sender]; @@ -495,7 +497,7 @@ contract BondedDeposits is IBondedDeposits, ReentrancyGuard, Initializable, Usin returns (uint256) { require(!isVoting(msg.sender)); - _redeemRewards(msg.sender); + // _redeemRewards(msg.sender); require(value > 0 && increase > 0); Account storage account = accounts[msg.sender]; Deposit storage bonded = account.deposits.bonded[noticePeriod]; From c192cddfb11ecca7c1550406def7bd4473e63be7 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Tue, 23 Jul 2019 13:57:38 -0700 Subject: [PATCH 02/41] Move contracts and imports (#90) --- .../common/{ => proxies}/GasCurrencyWhitelistProxy.sol | 2 +- .../contracts/common/{ => proxies}/GasPriceMinimumProxy.sol | 2 +- .../protocol/contracts/common/{ => proxies}/GoldTokenProxy.sol | 2 +- .../protocol/contracts/common/{ => proxies}/MultiSigProxy.sol | 2 +- .../protocol/contracts/common/{ => proxies}/RegistryProxy.sol | 2 +- .../contracts/governance/{ => proxies}/BondedDepositsProxy.sol | 2 +- .../contracts/governance/{ => proxies}/GovernanceProxy.sol | 2 +- .../contracts/governance/{ => proxies}/ValidatorsProxy.sol | 2 +- .../contracts/identity/{ => proxies}/AttestationsProxy.sol | 2 +- .../protocol/contracts/identity/{ => proxies}/EscrowProxy.sol | 2 +- .../protocol/contracts/identity/{ => proxies}/RandomProxy.sol | 2 +- .../contracts/stability/{ => proxies}/ExchangeProxy.sol | 2 +- .../protocol/contracts/stability/{ => proxies}/ReserveProxy.sol | 2 +- .../contracts/stability/{ => proxies}/SortedOraclesProxy.sol | 2 +- .../contracts/stability/{ => proxies}/StableTokenProxy.sol | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) rename packages/protocol/contracts/common/{ => proxies}/GasCurrencyWhitelistProxy.sol (79%) rename packages/protocol/contracts/common/{ => proxies}/GasPriceMinimumProxy.sol (78%) rename packages/protocol/contracts/common/{ => proxies}/GoldTokenProxy.sol (77%) rename packages/protocol/contracts/common/{ => proxies}/MultiSigProxy.sol (81%) rename packages/protocol/contracts/common/{ => proxies}/RegistryProxy.sol (81%) rename packages/protocol/contracts/governance/{ => proxies}/BondedDepositsProxy.sol (76%) rename packages/protocol/contracts/governance/{ => proxies}/GovernanceProxy.sol (75%) rename packages/protocol/contracts/governance/{ => proxies}/ValidatorsProxy.sol (75%) rename packages/protocol/contracts/identity/{ => proxies}/AttestationsProxy.sol (76%) rename packages/protocol/contracts/identity/{ => proxies}/EscrowProxy.sol (75%) rename packages/protocol/contracts/identity/{ => proxies}/RandomProxy.sol (75%) rename packages/protocol/contracts/stability/{ => proxies}/ExchangeProxy.sol (75%) rename packages/protocol/contracts/stability/{ => proxies}/ReserveProxy.sol (75%) rename packages/protocol/contracts/stability/{ => proxies}/SortedOraclesProxy.sol (76%) rename packages/protocol/contracts/stability/{ => proxies}/StableTokenProxy.sol (75%) diff --git a/packages/protocol/contracts/common/GasCurrencyWhitelistProxy.sol b/packages/protocol/contracts/common/proxies/GasCurrencyWhitelistProxy.sol similarity index 79% rename from packages/protocol/contracts/common/GasCurrencyWhitelistProxy.sol rename to packages/protocol/contracts/common/proxies/GasCurrencyWhitelistProxy.sol index 5782a7b33b0..7c81a99ec90 100644 --- a/packages/protocol/contracts/common/GasCurrencyWhitelistProxy.sol +++ b/packages/protocol/contracts/common/proxies/GasCurrencyWhitelistProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/common/GasPriceMinimumProxy.sol b/packages/protocol/contracts/common/proxies/GasPriceMinimumProxy.sol similarity index 78% rename from packages/protocol/contracts/common/GasPriceMinimumProxy.sol rename to packages/protocol/contracts/common/proxies/GasPriceMinimumProxy.sol index 9afb0d5ffc1..d0bcdfbeee3 100644 --- a/packages/protocol/contracts/common/GasPriceMinimumProxy.sol +++ b/packages/protocol/contracts/common/proxies/GasPriceMinimumProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/common/GoldTokenProxy.sol b/packages/protocol/contracts/common/proxies/GoldTokenProxy.sol similarity index 77% rename from packages/protocol/contracts/common/GoldTokenProxy.sol rename to packages/protocol/contracts/common/proxies/GoldTokenProxy.sol index 04e2a94f3cc..0422ad840fa 100644 --- a/packages/protocol/contracts/common/GoldTokenProxy.sol +++ b/packages/protocol/contracts/common/proxies/GoldTokenProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/common/MultiSigProxy.sol b/packages/protocol/contracts/common/proxies/MultiSigProxy.sol similarity index 81% rename from packages/protocol/contracts/common/MultiSigProxy.sol rename to packages/protocol/contracts/common/proxies/MultiSigProxy.sol index f6f7d55700e..682427055f8 100644 --- a/packages/protocol/contracts/common/MultiSigProxy.sol +++ b/packages/protocol/contracts/common/proxies/MultiSigProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "./Proxy.sol"; +import "../Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/common/RegistryProxy.sol b/packages/protocol/contracts/common/proxies/RegistryProxy.sol similarity index 81% rename from packages/protocol/contracts/common/RegistryProxy.sol rename to packages/protocol/contracts/common/proxies/RegistryProxy.sol index c39dd6e3092..c06b188499e 100644 --- a/packages/protocol/contracts/common/RegistryProxy.sol +++ b/packages/protocol/contracts/common/proxies/RegistryProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "./Proxy.sol"; +import "../Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/governance/BondedDepositsProxy.sol b/packages/protocol/contracts/governance/proxies/BondedDepositsProxy.sol similarity index 76% rename from packages/protocol/contracts/governance/BondedDepositsProxy.sol rename to packages/protocol/contracts/governance/proxies/BondedDepositsProxy.sol index c6a6d624317..a81221780c6 100644 --- a/packages/protocol/contracts/governance/BondedDepositsProxy.sol +++ b/packages/protocol/contracts/governance/proxies/BondedDepositsProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../../common/Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/governance/GovernanceProxy.sol b/packages/protocol/contracts/governance/proxies/GovernanceProxy.sol similarity index 75% rename from packages/protocol/contracts/governance/GovernanceProxy.sol rename to packages/protocol/contracts/governance/proxies/GovernanceProxy.sol index af576cf0c57..7bd5557f6b1 100644 --- a/packages/protocol/contracts/governance/GovernanceProxy.sol +++ b/packages/protocol/contracts/governance/proxies/GovernanceProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../../common/Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/governance/ValidatorsProxy.sol b/packages/protocol/contracts/governance/proxies/ValidatorsProxy.sol similarity index 75% rename from packages/protocol/contracts/governance/ValidatorsProxy.sol rename to packages/protocol/contracts/governance/proxies/ValidatorsProxy.sol index ce1dd49360f..17ea716bbaf 100644 --- a/packages/protocol/contracts/governance/ValidatorsProxy.sol +++ b/packages/protocol/contracts/governance/proxies/ValidatorsProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../../common/Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/identity/AttestationsProxy.sol b/packages/protocol/contracts/identity/proxies/AttestationsProxy.sol similarity index 76% rename from packages/protocol/contracts/identity/AttestationsProxy.sol rename to packages/protocol/contracts/identity/proxies/AttestationsProxy.sol index 126354c7cdd..79813528d45 100644 --- a/packages/protocol/contracts/identity/AttestationsProxy.sol +++ b/packages/protocol/contracts/identity/proxies/AttestationsProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../../common/Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/identity/EscrowProxy.sol b/packages/protocol/contracts/identity/proxies/EscrowProxy.sol similarity index 75% rename from packages/protocol/contracts/identity/EscrowProxy.sol rename to packages/protocol/contracts/identity/proxies/EscrowProxy.sol index d6668df377f..007f3115664 100644 --- a/packages/protocol/contracts/identity/EscrowProxy.sol +++ b/packages/protocol/contracts/identity/proxies/EscrowProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../../common/Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/identity/RandomProxy.sol b/packages/protocol/contracts/identity/proxies/RandomProxy.sol similarity index 75% rename from packages/protocol/contracts/identity/RandomProxy.sol rename to packages/protocol/contracts/identity/proxies/RandomProxy.sol index 1ddaac7fee9..b1ad0ddfe3f 100644 --- a/packages/protocol/contracts/identity/RandomProxy.sol +++ b/packages/protocol/contracts/identity/proxies/RandomProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../../common/Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/stability/ExchangeProxy.sol b/packages/protocol/contracts/stability/proxies/ExchangeProxy.sol similarity index 75% rename from packages/protocol/contracts/stability/ExchangeProxy.sol rename to packages/protocol/contracts/stability/proxies/ExchangeProxy.sol index 1924166397f..66121588844 100644 --- a/packages/protocol/contracts/stability/ExchangeProxy.sol +++ b/packages/protocol/contracts/stability/proxies/ExchangeProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../../common/Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/stability/ReserveProxy.sol b/packages/protocol/contracts/stability/proxies/ReserveProxy.sol similarity index 75% rename from packages/protocol/contracts/stability/ReserveProxy.sol rename to packages/protocol/contracts/stability/proxies/ReserveProxy.sol index 30fbece07ec..c4bb66435cf 100644 --- a/packages/protocol/contracts/stability/ReserveProxy.sol +++ b/packages/protocol/contracts/stability/proxies/ReserveProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../../common/Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/stability/SortedOraclesProxy.sol b/packages/protocol/contracts/stability/proxies/SortedOraclesProxy.sol similarity index 76% rename from packages/protocol/contracts/stability/SortedOraclesProxy.sol rename to packages/protocol/contracts/stability/proxies/SortedOraclesProxy.sol index 708eb314f74..bfeeae19221 100644 --- a/packages/protocol/contracts/stability/SortedOraclesProxy.sol +++ b/packages/protocol/contracts/stability/proxies/SortedOraclesProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../../common/Proxy.sol"; /* solhint-disable no-empty-blocks */ diff --git a/packages/protocol/contracts/stability/StableTokenProxy.sol b/packages/protocol/contracts/stability/proxies/StableTokenProxy.sol similarity index 75% rename from packages/protocol/contracts/stability/StableTokenProxy.sol rename to packages/protocol/contracts/stability/proxies/StableTokenProxy.sol index e1b92d9be80..ebe775cc0c5 100644 --- a/packages/protocol/contracts/stability/StableTokenProxy.sol +++ b/packages/protocol/contracts/stability/proxies/StableTokenProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.8; -import "../common/Proxy.sol"; +import "../../common/Proxy.sol"; /* solhint-disable no-empty-blocks */ From 6ec4ee77558931f7cfd7050174f92c46c8d3ea50 Mon Sep 17 00:00:00 2001 From: Aaron DeRuvo Date: Tue, 23 Jul 2019 14:28:30 -0700 Subject: [PATCH 03/41] [web] Update Top Banner for Post Alfajores (#119) * Post launch banner update * fix wrapping issue on mobile --- packages/web/src/header/BlueBanner.tsx | 16 ++++++---------- packages/web/static/locales/en/common.json | 3 ++- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/web/src/header/BlueBanner.tsx b/packages/web/src/header/BlueBanner.tsx index 2ae3502d99c..5b2b7c873b9 100644 --- a/packages/web/src/header/BlueBanner.tsx +++ b/packages/web/src/header/BlueBanner.tsx @@ -20,11 +20,9 @@ export class BlueBanner extends React.PureComponent { href={this.props.link} style={[fonts.navigation, textStyles.medium, styles.text]} > - - {this.props.children} - - - + {this.props.children} + + @@ -61,10 +59,6 @@ const styles = StyleSheet.create({ color: colors.white, lineHeight: 20, }, - nowrap: { - // @ts-ignore-next-line - whiteSpace: 'nowrap', - }, icon: { paddingLeft: 5, position: 'relative', @@ -73,5 +67,7 @@ const styles = StyleSheet.create({ }) export default withNamespaces('common')(({ t }: I18nProps) => ( - {t('blueBanner')} + + {t('blueBanner')} + )) diff --git a/packages/web/static/locales/en/common.json b/packages/web/static/locales/en/common.json index e95f1f741c0..f5bb022a8b4 100644 --- a/packages/web/static/locales/en/common.json +++ b/packages/web/static/locales/en/common.json @@ -24,5 +24,6 @@ "cookiesDisagree": "disagree", "trueGold": ["真金不怕火煉", "True gold fears no fire"], "copyRight": "Designed & Built by C Labs, © Celo 2019", - "blueBanner": "Join us for our special global announcement" + "blueBanner": + "Introducing Alfajores: Celo’s code is open source and the network is live for testing" } From c07391ec137548a141b3aa09cc8bcfcf4288ed74 Mon Sep 17 00:00:00 2001 From: Aaron DeRuvo Date: Tue, 23 Jul 2019 14:56:45 -0700 Subject: [PATCH 04/41] [web] Faucet and Invite Improvements (#64) At a high level this changes how the browser express and firebase client interact and makes UI improvements that are backed by those changes. Functional Changes A) Changes express to respond as soon as we have a key returned from firebase client (rather than waiting for entire faucet/invite request to complete or fail B) Use this key in browser to subscribe to updates from firebase about status of request C) give more granular feedback in the UI based on this extra info Refactors A) combine our Invite and Fauceting components into RequestFunds component B) combine our /invite and /faucet controllers C) remove start*Request convenience functions as they were less convenient once i combined the route handlers Tested with my own number and with my testnet address --- packages/web/server/FirebaseClient.ts | 62 +++--- packages/web/server/controllers.ts | 14 ++ packages/web/server/index.ts | 26 +-- packages/web/src/dev/FaucetPage.tsx | 128 +------------ packages/web/src/download/Cover.tsx | 6 +- packages/web/src/download/Invite.tsx | 139 -------------- packages/web/src/download/PhoneIllo.tsx | 2 +- .../web/src/fauceting/MicroComponents.tsx | 158 +++++++++++++++ packages/web/src/fauceting/RequestFunds.tsx | 180 ++++++++++++++++++ packages/web/src/fauceting/utils.ts | 58 ++++++ packages/web/src/icons/Checkmark.tsx | 21 ++ packages/web/static/locales/en/faucet.json | 12 +- 12 files changed, 483 insertions(+), 323 deletions(-) create mode 100644 packages/web/server/controllers.ts delete mode 100644 packages/web/src/download/Invite.tsx create mode 100644 packages/web/src/fauceting/MicroComponents.tsx create mode 100644 packages/web/src/fauceting/RequestFunds.tsx create mode 100644 packages/web/src/fauceting/utils.ts create mode 100644 packages/web/src/icons/Checkmark.tsx diff --git a/packages/web/server/FirebaseClient.ts b/packages/web/server/FirebaseClient.ts index 4d9d83afd7a..8b4c5a50b0b 100644 --- a/packages/web/server/FirebaseClient.ts +++ b/packages/web/server/FirebaseClient.ts @@ -10,12 +10,17 @@ function getFirebase() { return firebase } +function getDB() { + return getFirebase().database() +} + // Don't do this. It hangs next.js build process: https://github.com/zeit/next.js/issues/6824 // const db = firebase.database() const NETWORK = 'alfajores' export type Address = string +export type E164Number = string export enum RequestStatus { Pending = 'Pending', @@ -30,56 +35,39 @@ export enum RequestType { } export interface RequestRecord { - beneficiary: Address + beneficiary: Address | E164Number status: RequestStatus - txHash?: string type: RequestType + dollarTxHash?: string + goldTxHash?: string + escrowTxHash?: string // only on Invites } -const noop = () => { - /* noop*/ -} - -async function sendRequest( - beneficiary: Address, - type: RequestType, - onChange: (request: RequestRecord) => void -) { +export async function sendRequest(beneficiary: Address | E164Number, type: RequestType) { const newRequest: RequestRecord = { beneficiary, status: RequestStatus.Pending, type, } - - onChange(newRequest) - const ref = await getFirebase() - .database() + const ref = await getDB() .ref(`${NETWORK}/requests`) .push(newRequest) - return new Promise((resolve) => { - const listener = ref.on('value', (snap) => { - const record = snap.val() as RequestRecord - onChange(record) - - if (record.status === RequestStatus.Done || record.status === RequestStatus.Failed) { - ref.off('value', listener) - resolve(record) - } - }) - }) + return ref.key } -export async function startFundRequest( - beneficiary: Address, - onChange: (request: RequestRecord) => void = noop -) { - return sendRequest(beneficiary, RequestType.Faucet, onChange) -} +export async function subscribeRequest(key: string, onChange: (record: RequestRecord) => void) { + const ref = await getDB().ref(`${NETWORK}/requests/${key}`) + + const listener = ref.on('value', (snap) => { + const record = snap.val() as RequestRecord -export async function startInviteRequest( - beneficiary: Address, // This is actually a phone number - onChange: (request: RequestRecord) => void = noop -) { - return sendRequest(beneficiary, RequestType.Invite, onChange) + if (record) { + onChange(record) + } + + if (record.status === RequestStatus.Done || record.status === RequestStatus.Failed) { + ref.off('value', listener) + } + }) } diff --git a/packages/web/server/controllers.ts b/packages/web/server/controllers.ts new file mode 100644 index 00000000000..993cab320bf --- /dev/null +++ b/packages/web/server/controllers.ts @@ -0,0 +1,14 @@ +import { Request, Response } from 'express' +import captchaVerify from './captchaVerify' +import { RequestStatus, RequestType, sendRequest } from './FirebaseClient' + +export async function faucetOrInviteController(req: Request, res: Response, type: RequestType) { + const { captchaToken, beneficiary } = req.body + const captchaResponse = await captchaVerify(captchaToken) + if (captchaResponse.success) { + const key = await sendRequest(beneficiary, type) + res.status(200).json({ status: RequestStatus.Pending, key }) + } else { + res.status(401).json({ status: RequestStatus.Failed }) + } +} diff --git a/packages/web/server/index.ts b/packages/web/server/index.ts index 3d30e5e6ff1..fbef4093b8b 100644 --- a/packages/web/server/index.ts +++ b/packages/web/server/index.ts @@ -7,9 +7,9 @@ import * as next from 'next' import nextI18NextMiddleware from 'next-i18next/middleware' import addToCRM from '../server/addToCRM' import nextI18next from '../src/i18n' -import captchaVerify from './captchaVerify' +import { faucetOrInviteController } from './controllers' import { submitFellowApp } from './FellowshipApp' -import { RequestStatus, startFundRequest, startInviteRequest } from './FirebaseClient' +import { RequestType } from './FirebaseClient' import mailer from './mailer' const port = parseInt(process.env.PORT, 10) || 3000 @@ -81,26 +81,12 @@ function wwwRedirect(req, res, nextAction) { res.status(204).send('ok') }) - server.post('/faucet', async (req, res) => { - const { captchaToken, beneficiary } = req.body - const captchaResponse = await captchaVerify(captchaToken) - if (captchaResponse.success) { - const funding = await startFundRequest(beneficiary) - res.status(200).json(funding) - } else { - res.status(401).json({ status: RequestStatus.Failed }) - } + server.post('/faucet', (req, res) => { + faucetOrInviteController(req, res, RequestType.Faucet) }) - server.post('/invite', async (req, res) => { - const { captchaToken, beneficiary } = req.body - const captchaResponse = await captchaVerify(captchaToken) - if (captchaResponse.success) { - const funding = await startInviteRequest(beneficiary) - res.status(200).json(funding) - } else { - res.status(401).json({ status: RequestStatus.Failed }) - } + server.post('/invite', (req, res) => { + faucetOrInviteController(req, res, RequestType.Invite) }) server.post('/contacts', async (req, res) => { diff --git a/packages/web/src/dev/FaucetPage.tsx b/packages/web/src/dev/FaucetPage.tsx index 31626184e00..ddf7966ce0f 100644 --- a/packages/web/src/dev/FaucetPage.tsx +++ b/packages/web/src/dev/FaucetPage.tsx @@ -1,135 +1,39 @@ -import getConfig from 'next/config' import * as React from 'react' -import ReCAPTCHA from 'react-google-recaptcha' -import { ActivityIndicator, StyleSheet, Text, View } from 'react-native' +import { StyleSheet, Text, View } from 'react-native' +import RequestFunds from 'src/fauceting/RequestFunds' +import { RequestState } from 'src/fauceting/utils' import { H1 } from 'src/fonts/Fonts' -import { TextInput } from 'src/forms/FormComponents' import OpenGraph from 'src/header/OpenGraph' import { I18nProps, NameSpaces, Trans, withNamespaces } from 'src/i18n' import SideTitledSection from 'src/layout/SideTitledSection' import Button, { BTN, SIZE } from 'src/shared/Button.3' -import { postForm } from 'src/shared/Form' import { CeloLinks } from 'src/shared/menu-items' import { HEADER_HEIGHT } from 'src/shared/Styles' import { colors, fonts, standardStyles, textStyles } from 'src/styles' -import { RequestStatus } from '../../server/FirebaseClient' - -enum RequestState { - Initial, - Invalid, - Started, - Completed, - Failed, -} - +import { RequestType } from '../../server/FirebaseClient' interface State { address: string requestState: RequestState isCaptchaValid: boolean } -function faucet({ captchaToken, address }) { - return postForm('/faucet', { captchaToken, beneficiary: address }) -} - class FaucetPage extends React.Component { static getInitialProps = () => { return { namespacesRequired: [NameSpaces.faucet, NameSpaces.common], } } - recaptchaRef = React.createRef() - - state = { - address: '', - requestState: RequestState.Initial, - isCaptchaValid: false, - } - - onTyping = ({ nativeEvent }) => { - const { value } = nativeEvent.target - this.setState({ - address: value, - requestState: - this.state.requestState !== RequestState.Started - ? RequestState.Initial - : this.state.requestState, - }) - } - - onCaptcha = (value: string | null) => { - this.setState({ isCaptchaValid: !!value }) - } - - requestFaucet = async () => { - if (!(this.state.address.length > 0)) { - this.setState({ requestState: RequestState.Invalid }) - return - } - - this.setState({ requestState: RequestState.Started }) - - const captchaToken = this.recaptchaRef.current.getValue() - const res = await faucet({ captchaToken, address: this.state.address }) - const status = (await res.json()).status as RequestStatus - - if (status === RequestStatus.Done) { - this.setState({ requestState: RequestState.Completed }) - } else { - this.setState({ requestState: RequestState.Failed }) - } - } - - getCaptchaKey = () => { - return getConfig().publicRuntimeConfig.RECAPTCHA - } render() { const { t } = this.props - const { requestState } = this.state - const hasFailed = requestState === RequestState.Failed - const isInvalid = requestState === RequestState.Invalid - const isComplete = requestState === RequestState.Completed - const isStarted = this.state.requestState === RequestState.Started return ( <> - +

{t('title')}

- - - - - - -