From 7df6ea70e4469a8cb8d8dabf4f8039a932fdf56d Mon Sep 17 00:00:00 2001 From: Makoto Inoue <2630+makoto@users.noreply.github.com> Date: Sat, 10 Oct 2020 19:16:56 +0100 Subject: [PATCH 01/30] Integration 2 (#392) * Support clearAndSend * Update contracts * Add clearFee and withdrawn * WIP: sendAndWithdrawPayout * endOfCoolingPeriod is null if not ended * Add timezone * rewording * Pass owner address to contribution address * Bump contract version to 1.4.2 * Avoid float * Integration 2 donation (#379) * WIP: sendAndWithdrawPayout * endOfCoolingPeriod is null if not ended * Add timezone * rewording * Pass owner address to contribution address * Bump contract version to 1.4.2 * Remove console.log * Get Token symbol from server side (and support XDAI) (#383) * Add event contribution data * Upgrade contracts-integration to 1.4.5 with new xDai deployer address * Add deploy:xdai * Add build:release:xdai * Set gasPrice to 1gwei * Set blockscout for xDai * Add config * Increase contribution precesion * Add warning and fix user profile link * Adjust denomination * Add gift information on participant page * Add GET_CONTRIBUTIONS_BY_PARTY * Contribute to Cooper * Hide private info * Fix the rounding issue * Do not pass argument to withdraw * Add optional.recepients * Add address field to recepient * Add support to handle ERC20 tokens * Upgrade bnc-onboard * Re-enabled Gitcoin CLR banner * fix timezone styling * Add before and after based on createdAt * Switch from now to vercel * Deploy to xdai when master is merged * Remove alias from json file * Add -t * Fix eslint errors --- .deploy/now.rinkeby.json | 3 +- .gitignore | 2 + package.json | 13 +- scripts/setup.js | 10 + src/GlobalState.js | 2 +- src/api/resolvers/singleEventResolvers.js | 94 +- src/api/resolvers/tokenResolvers.js | 14 +- src/components/Links/EtherScanLink.js | 11 +- .../SingleEvent/Admin/ClearAndSend.js | 51 + .../SingleEvent/Admin/CurrencyPicker.js | 32 +- src/components/SingleEvent/Admin/PartyForm.js | 527 +++--- .../Admin/SmartContractFunctions.js | 75 +- src/components/SingleEvent/Contribute.js | 75 + src/components/SingleEvent/Currency.js | 2 +- src/components/SingleEvent/EventCTA.js | 229 ++- .../SingleEvent/EventParticipants.js | 72 +- src/components/SingleEvent/Participant.js | 35 +- .../SingleEvent/ParticipantStatus.js | 5 + .../SingleEvent/ParticipantTableList.js | 41 +- src/components/SingleEvent/SendAndWithdraw.js | 31 + src/components/SingleEvent/WithdrawPayout.js | 4 +- src/components/UserProfile/UserProfile.js | 49 + src/graphql/fragments.js | 26 + src/graphql/mutations.js | 14 + src/graphql/queries.js | 31 +- src/layout/Banner.js | 2 +- src/layout/Header.js | 4 +- src/routes/SingleEventAdmin.js | 13 +- src/schema.gql | 1 + yarn.lock | 1531 ++++++++++++----- 30 files changed, 2182 insertions(+), 817 deletions(-) create mode 100644 src/components/SingleEvent/Admin/ClearAndSend.js create mode 100644 src/components/SingleEvent/Contribute.js create mode 100644 src/components/SingleEvent/SendAndWithdraw.js diff --git a/.deploy/now.rinkeby.json b/.deploy/now.rinkeby.json index 916405c3..06247eda 100644 --- a/.deploy/now.rinkeby.json +++ b/.deploy/now.rinkeby.json @@ -1,7 +1,6 @@ { "version": 2, - "name": "kickback-app-rinkeby", - "alias": "rinkeby.kickback.events", + "scope": "wearekickback", "builds": [ { "src": "build/**", "use": "@now/static" }, { "src": "api/**", "use": "@now/node" } diff --git a/.gitignore b/.gitignore index 556c4580..c0948b1c 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ tmp/* # IDE .idea .vscode + +.vercel diff --git a/package.json b/package.json index e56a88b1..bd97e3e3 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "@emotion/core": "^10.0.27", "@emotion/styled": "^10.0.27", "@ensdomains/renewal-widget": "^0.1.9", - "@wearekickback/contracts": "^1.2.9", + "@wearekickback/contracts": "npm:@wearekickback/contracts-integration@1.4.5", "@wearekickback/shared": "^1.14.1", "apollo-cache-inmemory": "^1.2.8", "apollo-client": "^2.4.0", @@ -15,7 +15,7 @@ "apollo-link-state": "^0.4.1", "apollo-upload-client": "^10.0.0", "apollo-utilities": "^1.0.21", - "bnc-onboard": "^1.7.1", + "bnc-onboard": "^1.11.0", "decimal.js": "^10.0.1", "es6-promisify": "^6.0.0", "ethereum-event-logs": "^1.0.2", @@ -52,9 +52,10 @@ "react-tooltip": "^3.9.0", "sanitize-html": "^1.20.1", "smart-chat-react": "^0.1.15", - "truffle": "^5.0.0-beta.0", + "truffle": "^5.1.30", "use-interval": "^1.2.1", "uuid": "^3.3.2", + "vercel": "^20.1.0", "web3": "1.0.0-beta.36", "web3-utils": "^1.0.0-beta.36", "whatwg-fetch": "^3.0.0" @@ -69,13 +70,15 @@ "build:release:rinkeby": "yarn setup --rinkeby && yarn build", "build:release:alpha": "yarn setup --alpha && yarn build", "build:release:live": "yarn setup --live && yarn build", + "build:release:xdai": "yarn setup --xdai && yarn build", "deploy:ropsten": "yarn build:release:ropsten && yarn now -f --local-config .deploy/now.ropsten.json --public && yarn now alias --local-config .deploy/now.ropsten.json && yarn now rm kickback-app-ropsten --safe --yes", "deploy:rinkeby": "yarn build:release:rinkeby && yarn now -f --local-config .deploy/now.rinkeby.json --public && yarn now alias --local-config .deploy/now.rinkeby.json && yarn now rm kickback-app-rinkeby --safe --yes", "deploy:kovan": "yarn build:release:kovan && yarn now -f --local-config .deploy/now.kovan.json --public && yarn now alias --local-config .deploy/now.kovan.json && yarn now rm kickback-app-kovan --safe --yes", "deploy:alpha": "yarn build:release:alpha && yarn now -f --local-config .deploy/now.alpha.json --public && yarn now alias --local-config .deploy/now.alpha.json && yarn now rm kickback-app-alpha --safe --yes", "deploy:live": "yarn build:release:live && yarn now -f --local-config .deploy/now.live.json --public && yarn now alias --local-config .deploy/now.live.json && yarn now rm kickback-app-live --safe --yes", + "deploy:xdai": "yarn build:release:xdai && yarn now -f --local-config .deploy/now.xdai.json --public && yarn now alias --local-config .deploy/now.xdai.json && yarn now rm kickback-app-xdai --safe --yes", "deploy:dev": "yarn deploy:rinkeby", - "deploy:branch": "/bin/sh -c 'if [ \"$TRAVIS_BRANCH\" = \"master\" ]; then yarn deploy:live; elif [ \"$TRAVIS_BRANCH\" = \"dev\" ]; then yarn deploy:dev; fi'", + "deploy:branch": "/bin/sh -c 'if [ \"$TRAVIS_BRANCH\" = \"master\" ]; then yarn deploy:xdai; elif [ \"$TRAVIS_BRANCH\" = \"dev\" ]; then yarn deploy:dev; fi'", "deploy:pr": "yarn build:release:rinkeby && scripts/deployTravisBuildToSurge.sh", "deploy": "/bin/sh -c 'if [ \"$TRAVIS_PULL_REQUEST\" = \"false\" ]; then yarn deploy:branch; else yarn deploy:pr; fi'", "precommit": "lint-staged", @@ -85,7 +88,7 @@ "eject": "react-scripts eject", "storybook": "start-storybook -p 9009 -s public", "build-storybook": "build-storybook -s public", - "now": "now --team wearekickback --token $NOW_TOKEN" + "now": "vercel --scope wearekickback -t $NOW_TOKEN" }, "devDependencies": { "@storybook/addon-actions": "^3.4.11", diff --git a/scripts/setup.js b/scripts/setup.js index 51db7c2d..e34751da 100755 --- a/scripts/setup.js +++ b/scripts/setup.js @@ -71,6 +71,16 @@ if (argv.ropsten) { appConfig.FORTMATIC_KEY = 'pk_test_D3CAA2AEFE6A022E' appConfig.PORTIS_KEY = '0ae69aa0-2a4e-41b2-a312-4aa2de69626e' appConfig.SQUARELINK_KEY = '7918e26f77908d911fac' +} else if (argv.xdai) { + appConfig.ENV = 'xdai' + appConfig.API_URL = 'https://kickback-xdai.herokuapp.com' + appConfig.GIT_COMMIT = getGitCommit() + appConfig.ROLLBAR_TOKEN = '' + appConfig.BLOCKNATIVE_DAPPID = '' + appConfig.INFURA_KEY = 'cd1ba006128543a0a11d23e54efaab93' + appConfig.FORTMATIC_KEY = 'pk_test_D3CAA2AEFE6A022E' + appConfig.PORTIS_KEY = '0ae69aa0-2a4e-41b2-a312-4aa2de69626e' + appConfig.SQUARELINK_KEY = '7918e26f77908d911fac' } else if (argv.live) { appConfig.ENV = 'live' appConfig.API_URL = 'https://kickback-live.herokuapp.com' diff --git a/src/GlobalState.js b/src/GlobalState.js index 8618f83c..96150b1d 100644 --- a/src/GlobalState.js +++ b/src/GlobalState.js @@ -114,7 +114,7 @@ class Provider extends Component { } let { onboard } = this.state - + console.log('**setUpWallet', { networkId }) if (!onboard) { // dappid is mandatory so will have throw away id for local usage. let testid = 'c212885d-e81d-416f-ac37-06d9ad2cf5af' diff --git a/src/api/resolvers/singleEventResolvers.js b/src/api/resolvers/singleEventResolvers.js index 15742d36..33b28c75 100644 --- a/src/api/resolvers/singleEventResolvers.js +++ b/src/api/resolvers/singleEventResolvers.js @@ -14,6 +14,21 @@ export const defaults = { markedAttendedList: [] } +const getOption = async args => { + const account = await getAccount() + const option = { + from: account + } + // TODO: Do this only for xDai. + if (true) { + option.gasPrice = 1000000000 // 1gwei + } + return { + ...option, + ...args + } +} + const resolvers = { Party: { description: party => party.description_text || null, @@ -129,8 +144,7 @@ const resolvers = { let tokenAddress = args.tokenAddress const web3 = await getWeb3() - const account = await getAccount() - + const option = await getOption({ gas: 3000000 }) if (tokenAddress === '') { tokenAddress = EMPTY_ADDRESS } @@ -151,10 +165,7 @@ const resolvers = { toEthVal(coolingPeriod).toString(16), tokenAddress ) - .send({ - gas: 3000000, - from: account - }) + .send(option) .on('transactionHash', hash => { resolve(hash) }) @@ -173,14 +184,10 @@ const resolvers = { console.log(`Adding admins:\n${userAddresses.join('\n')}`) const web3 = await getWeb3() - const account = await getAccount() + const option = await getOption() const { methods: contract } = new web3.eth.Contract(abi, address) try { - const tx = await txHelper( - contract.grant(userAddresses).send({ - from: account - }) - ) + const tx = await txHelper(contract.grant(userAddresses).send(option)) return tx } catch (err) { @@ -192,7 +199,6 @@ const resolvers = { async rsvp(_, { address }) { let tokenAddress const web3 = await getWeb3() - const account = await getAccount() const { methods: contract } = new web3.eth.Contract(abi, address) try { tokenAddress = await contract.tokenAddress().call() @@ -205,13 +211,9 @@ const resolvers = { } else { deposit = await contract.deposit().call() } + const option = await getOption({ value: deposit }) try { - const tx = await txHelper( - contract.register().send({ - from: account, - value: deposit - }) - ) + const tx = await txHelper(contract.register().send(option)) return tx } catch (err) { console.error(err) @@ -221,13 +223,11 @@ const resolvers = { }, async finalize(_, { address, maps }) { const web3 = await getWeb3() - const account = await getAccount() + const option = await getOption() const { methods: contract } = new web3.eth.Contract(abi, address) try { const tx = await txHelper( - contract.finalize(maps.map(m => toBN(m).toString(10))).send({ - from: account - }) + contract.finalize(maps.map(m => toBN(m).toString(10))).send(option) ) return tx @@ -239,29 +239,43 @@ const resolvers = { }, async withdrawPayout(_, { address }) { const web3 = await getWeb3() - const account = await getAccount() + const option = await getOption() + const { methods: contract } = new web3.eth.Contract(abi, address) + try { + const tx = await txHelper(contract.withdraw().send(option)) + + return tx + } catch (err) { + console.error(err) + + throw new Error(`Failed to withdraw`) + } + }, + async sendAndWithdrawPayout(_, { address, addresses, values }) { + const web3 = await getWeb3() + const option = await getOption() const { methods: contract } = new web3.eth.Contract(abi, address) try { const tx = await txHelper( - contract.withdraw().send({ - from: account - }) + contract + .sendAndWithdraw(addresses, [toEthVal(values[0]).toFixed(0)]) + .send(option) ) return tx } catch (err) { console.error(err) - throw new Error(`Failed to withdraw`) + throw new Error(`Failed to send and withdraw`) } }, async setLimitOfParticipants(_, { address, limit }) { const web3 = await getWeb3() - const account = await getAccount() + const option = await getOption() const { methods: contract } = new web3.eth.Contract(abi, address) try { const tx = await txHelper( - contract.setLimitOfParticipants(limit).send({ from: account }) + contract.setLimitOfParticipants(limit).send(option) ) return tx @@ -272,14 +286,14 @@ const resolvers = { }, async changeDeposit(_, { address, deposit }) { const web3 = await getWeb3() - const account = await getAccount() + const option = await getOption() const { methods: contract } = new web3.eth.Contract(abi, address) const depositInWei = toEthVal(deposit, 'eth') .toWei() .toString(16) try { const tx = await txHelper( - contract.changeDeposit(depositInWei).send({ from: account }) + contract.changeDeposit(depositInWei).send(option) ) return tx @@ -290,11 +304,23 @@ const resolvers = { }, async clear(_, { address }) { const web3 = await getWeb3() - const account = await getAccount() + const option = await getOption() const { methods: contract } = new web3.eth.Contract(abi, address) try { - const tx = await txHelper(contract.clear().send({ from: account })) + const tx = await txHelper(contract.clear().send(option)) + return tx + } catch (e) { + console.log(e) + return null + } + }, + async clearAndSend(_, { address, num }) { + const web3 = await getWeb3() + const option = await getOption() + const { methods: contract } = new web3.eth.Contract(abi, address) + try { + const tx = await txHelper(contract.clearAndSend(num).send(option)) return tx } catch (e) { console.log(e) diff --git a/src/api/resolvers/tokenResolvers.js b/src/api/resolvers/tokenResolvers.js index 2dcb5eed..ff0d7f11 100644 --- a/src/api/resolvers/tokenResolvers.js +++ b/src/api/resolvers/tokenResolvers.js @@ -5,10 +5,6 @@ import DETAILEDERC20_ABI from '../abi/detailedERC20ABI' import DETAILEDERC20BYTES32_ABI from '../abi/detailedERC20bytes32ABI' import { txHelper, isEmptyAddress } from '../utils' export const defaults = {} -// This is because some token uses string as symbol while others are bytes32 -// We need some workaround like https://ethereum.stackexchange.com/questions/58945/how-to-handle-both-string-and-bytes32-method-returns when supporting any ERC20 - -let token const getTokenContract = (web3, address, abi) => { return new web3.eth.Contract(abi, address).methods @@ -59,13 +55,12 @@ const resolvers = { ) } }, - async getToken(_, { tokenAddress }) { - if (token) return token + async getClientToken(_, { tokenAddress }) { if (isEmptyAddress(tokenAddress)) { return { - name: 'Ether', - symbol: 'ETH', - decimals: 18 + name: null, + symbol: null, + decimals: null } } else if ( tokenAddress === '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359' @@ -99,7 +94,6 @@ const resolvers = { contract.symbol().call(), contract.decimals().call() ]) - // To fit in a bytes32 on the contract, token name and symbol // have been padded to length using null characters. // We then strip these characters using the regex `/\u0000/g` diff --git a/src/components/Links/EtherScanLink.js b/src/components/Links/EtherScanLink.js index 5385d9cb..29045498 100644 --- a/src/components/Links/EtherScanLink.js +++ b/src/components/Links/EtherScanLink.js @@ -23,15 +23,20 @@ const EtherScanLink = ({ address, tx, children }) => ( return tx } } - + let host const prefix = '1' === expectedNetworkId ? '' : `${expectedNetworkName.toLowerCase()}.` + if (expectedNetworkId === '100') { + host = `https://blockscout.com/poa/xdai` + } else { + host = `https://${prefix}etherscan.io` + } let link if (address) { - link = `https://${prefix}etherscan.io/address/${address}` + link = `${host}/address/${address}` } else if (tx) { - link = `https://${prefix}etherscan.io/tx/${tx}` + link = `${host}/tx/${tx}` } return link ? ( diff --git a/src/components/SingleEvent/Admin/ClearAndSend.js b/src/components/SingleEvent/Admin/ClearAndSend.js new file mode 100644 index 00000000..b8a188d5 --- /dev/null +++ b/src/components/SingleEvent/Admin/ClearAndSend.js @@ -0,0 +1,51 @@ +import React, { useState } from 'react' +import styled from '@emotion/styled' +import { Mutation } from 'react-apollo' +import gql from 'graphql-tag' + +import Button from '../../Forms/Button' +import DefaultTextInput from '../../Forms/TextInput' + +const TextInput = styled(DefaultTextInput)` + margin-bottom: 10px; +` + +const CLEAR_AND_SEND = gql` + mutation clearAndSend($address: String, $num: String) { + clearAndSend(address: $address, num: $num) @client + } +` + +const ClearAndSend = ({ address, num }) => { + const [text, setText] = useState('') + return ( + + + {clearAndSend => ( + <> + + + + + )} + + + ) +} + +const Container = styled('div')`` + +export default ClearAndSend diff --git a/src/components/SingleEvent/Admin/CurrencyPicker.js b/src/components/SingleEvent/Admin/CurrencyPicker.js index b70c9342..931efd2e 100644 --- a/src/components/SingleEvent/Admin/CurrencyPicker.js +++ b/src/components/SingleEvent/Admin/CurrencyPicker.js @@ -1,6 +1,6 @@ import React from 'react' -const CurrencyPicker = ({ onChange, currencyType }) => { +const CurrencyPicker = ({ onChange, currencyType, nativeCurrencyType }) => { const onChangeHandler = e => { currencyType = e.target.value onChange(currencyType) @@ -13,25 +13,29 @@ const CurrencyPicker = ({ onChange, currencyType }) => { return ( <> - + {nativeCurrencyType === 'ETH' ? ( + + ) : ( + '' + )}