From a1cc52762f36c0472f50373f2a60a81ba27531fc Mon Sep 17 00:00:00 2001 From: Jelle Date: Mon, 10 Feb 2020 11:57:14 +0100 Subject: [PATCH] Competition improvementx, xdai support (#1362) * update docker-compose * get rid of floats and margins for positioning in card * hook up to actual executed Competition proposals * work-around out-of-memory in webpack.dev.server * complete details html * upgrade subgraph version * fixed solutions row css * improvements to Card * more Card improvements * StatusBlob, css, links to client, tz datetimes * more in Details * implement create solution action * better solutions table css * blue border on selected solution * more on solution details, other css * a little more niceness in Details * connected solution vote to client lib * new proposal expectation text * more proposal expectation text * Solution => Suggestion * more Solution=> Submission * add spacing between cards * UI improvements and bug fixes for submshn details * more layout improvements * cleanup up schemeContainer layout a bit * pull in latest client lib * refactor suggestions queries, get splits * show user voted in details * detect whether current user voted in Subm Details * fix title and description on create * fix hang on competitions having 0 submissions * fix time options * ui taeaks, added Redeem * avoid clipping of user account popup * Alter logic for showing voting countdown * clean up schemecontainer layout * refactor modularity pattern * add Go To Competition link to ProposalDetails * supply dynamic crx contract name in utils * refactor to get crx rewarder name synchronously * a little cleanup refactoring * lint * scroll bars for create proposal modal * remove obsolete comment * fix suggestor account profile * fix breadcrumbs * fix breadcrumbs * fix display of voting countdown * fix card clipping on bottom with very long title * spacing tweak * add date picker * fix date picker * add time picker * update competion proposal validation * submission details from url * fix createproposal lint errors * use client 0.2.39 * fix reward split validation * update date-time picker * remove console log * tweaks to date picker layout * use client 0.2.40 * handle no submissions, css tweaks * enable use of proposal.scheme * update addresses, use latests test_env images * support for displaying submission tags * fix compile error, nowrap account names * add voting period countdown * fix huge url layout problem * fix validation error not showing * Rename inputs * add some more date time validations * fix lint error * no-winners text, auto update UI on phase changes * fix no winners text * add submissions to start countdown * to redeem, check if proposal is redeemed * use cleint 0.2.41 * start on winning voters * tweak creator css * more for isWinner * more on votes * show competition end datetime in card * winners feedback on cards * lint * use latest test_env images * fix dependency error * better subscriptions, little tweaks * refactor utils.getSubmissions * new subgraph, more refactoring, fixes, changes * status changes * reenable tags * add CompetitionVote.search by competition * variable name correction * fix compile error * try for fixing repfromtoken * fix rep redeem for privateKey case * fix submission creator account * fix thumbsup icon tooltip * fix compile error * add needed subscriptions for SubDetails * fix bug that crashes competition page * fix compile error * update test code * make staging code build not run out of memory * nicer vote button * automatically update status in SubmissionDetails * fix compile error * show competition regardless of passed or executed * restrict redeem button just on GP redemptions * fix compile error * handle no logged-in account * gate new submissions until proposal is executed * xperiment * another try to fix wrap * clean up subdetails layout a little when sparse * show countdown when inbetween subm and voting * fix submission row corner styling * better wrapping on rep in right section * upgrade to client 2.44 * fix submissions query * use masterrinkeby * add scrollbar to description * replaced datepicker * comp start time should be shown as required * try at test fix * css fixes * fix automated test * show rep as percentage * first phase of Competition card sorting * more efficient fetching of status by Cards * removed lint disably thing * a little code cleanup * sort tied cards by stage dates * add CountdownText component * fix package errors that break the build * merge dev * a couple requested text changes * countdown styling * status tracking issues, refactor account votes * subm created date. triangle for current phase * more improvements to status logic * max width of images in subscription descriptions * start on createproposal mobility * use grid css to align in createproposal modal * tweak CreateProposal CSS * more mobile * use fr units * start on switching submissions list to grid * remove comments, lint warnings * start on mobile detais/submissions * undo false start * more mobility in details * fix compile error * upgrade client to latest migration * update ganache subgraph * use vote.submission * capture votes. proposerIsAdmin, customBeneficiary * remove comments * mobilize the submissions list * SubmissionDetails mobility * merge add'l dev change * better bluebuttons * more submissionDetal mobility * mobilize create submission modal * wording change * no scroll description when no submissions * the merge fixes that I forgot to commit earlier * new datetime calendar * fix tests ( :-D !) * IStateProps=>IState * IExternalStateProps=>IExternalState * classname import * added comments * cleanup changelog.md * add braces * statusblob comment * removed comments * more changelog * fixing mixpanel base url passing * fix mixpanel problems (hopefully) * undo changes that should not have been committed * lint * add comment in moment-timezone * show beneficiary instead of suggester * Fix staking errors Get staking working Make sure error doesnt overlap amount needed to boost text Fixes: https://github.com/daostack/alchemy/issues/1350 * mobile glitch at bottom of subm det on iPhone 5 * mobiliity tweaks * restore submission details url * add disqus to submission details (#1358) * add disqus to submission details * use submission, not proposal, title * web3connect/burnerwallet (#1353) * forgot to commit package.json * validate for invalid datetime formatting * add disqus to CompetitionDetails * xdai support (#1372) * tweaks to help disqus * remove disqus from submission details * nicer formating on submissions heading * tweak width of winners badge * tweak redeem button * fix xdai settings * fix mainnet connection identifier * start on high-level caching * supress build dev errors * set correct xdai endpoints * optimize getSubmission * optimize getCompetitionVotes * more preparation of queries for caching * removed redundant code * upgrade xdao subgraph to v37_3_xdai * added a comment * upgrade burnerwallet * When on xdai network ETH becomes xDAI and GEN becomes xGEN (#1380) * show only registred daos in xdai network * new xDao subgraph * fix tokens.json for production * lint * disable new submission button and show tooltip * add torus web provider * prevent new submission click when disabled * undo add Torus * switch to absolute rep from percentage * fix compile errors Co-authored-by: Doug Kent Co-authored-by: Shivani Co-authored-by: Tibet Sprague --- data/tokens.json | 129 +++-- package-lock.json | 272 ++++++---- package.json | 4 +- src/arc.ts | 492 ++++++------------ src/components/Account/AccountBalances.tsx | 6 +- src/components/Account/AccountProfilePage.tsx | 6 +- src/components/Daos/DaosPage.tsx | 6 +- .../CreateContributionRewardProposal.tsx | 6 +- .../CreateUnknownGenericSchemeProposal.tsx | 6 +- src/components/Proposal/RedemptionsString.tsx | 6 +- src/components/Proposal/RedemptionsTip.tsx | 8 +- src/components/Proposal/RewardsString.tsx | 4 +- .../Redemptions/RedemptionsPage.tsx | 6 +- .../Competition/Card.tsx | 2 +- .../Competition/Competitions.scss | 88 ++-- .../Competition/CreateProposal.tsx | 39 +- .../Competition/Details.tsx | 69 ++- .../Competition/List.tsx | 18 +- .../Competition/SubmissionDetails.tsx | 34 +- .../Competition/utils.ts | 53 +- src/layouts/AppContainer.tsx | 5 +- src/layouts/Header.tsx | 4 +- src/layouts/SidebarMenu.tsx | 8 +- src/lib/util.ts | 27 +- src/settings.ts | 121 ++++- test/integration/utils.ts | 13 +- typings/walletconnect.d.ts | 1 + webpack.base.config.js | 9 +- 28 files changed, 823 insertions(+), 619 deletions(-) create mode 100644 typings/walletconnect.d.ts diff --git a/data/tokens.json b/data/tokens.json index 28b6ff3c0..415b4d5d5 100644 --- a/data/tokens.json +++ b/data/tokens.json @@ -1,47 +1,94 @@ { - "0x6b175474e89094c44da98b954eedeac495271d0f": { - "decimals": 18, - "name": "Dai Stablecoin", - "symbol": "DAI" + "ganache": { + "baseTokenName": "ETH", + "genName": "GEN", + "tokens": {} }, - "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359": { - "decimals": 18, - "name": "Sai Stablecoin v1.0", - "symbol": "SAI" + "main": { + "baseTokenName": "ETH", + "genName": "GEN", + "tokens": { + "0x6b175474e89094c44da98b954eedeac495271d0f": { + "decimals": 18, + "name": "Dai Stablecoin", + "symbol": "DAI" + }, + "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359": { + "decimals": 18, + "name": "Sai Stablecoin v1.0", + "symbol": "SAI" + }, + "0x056fd409e1d7a124bd7017459dfea2f387b6d5cd": { + "decimals": 2, + "name": "Gemini dollar", + "symbol": "GUSD" + }, + "0xdd974d5c2e2928dea5f71b9825b8b646686bd200": { + "decimals": 18, + "name": "KyberNetwork", + "symbol": "KNC" + }, + "0x8e870d67f660d95d5be530380d0ec0bd388289e1": { + "decimals": 18, + "name": "Paxos Standard", + "symbol": "PAX" + }, + "0x0000000000085d4780B73119b644AE5ecd22b376": { + "decimals": 18, + "name": "TrueUSD", + "symbol": "TUSD" + }, + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": { + "decimals": 6, + "name": "USD Coin", + "symbol": "USDC" + }, + "0xdac17f958d2ee523a2206206994597c13d831ec7": { + "decimals": 6, + "name": "Tether USD", + "symbol": "USDT" + }, + "0xCc80C051057B774cD75067Dc48f8987C4Eb97A5e": { + "decimals": 18, + "name": "Nectar", + "symbol": "NEC" + } + } }, - "0x056fd409e1d7a124bd7017459dfea2f387b6d5cd": { - "decimals": 2, - "name": "Gemini dollar", - "symbol": "GUSD" + "rinkeby": { + "baseTokenName": "ETH", + "genName": "GEN", + "tokens": { + "0x6f2d6ff85efca691aad23d549771160a12f0a0fc": { + "decimals": 18, + "name": "Dai Stablecoin", + "symbol": "DAI" + }, + "0x811e5cc5fddd395d488fe92c4f0f917f3ada6ab6": { + "decimals": 18, + "name": "Sai Stablecoin v1.0", + "symbol": "SAI" + }, + "0x6cbe14d7af0fc86c59f125c6506817d91d412582": { + "decimals": 2, + "name": "Gemini dollar", + "symbol": "GUSD" + }, + "0x9f13dbe2ef9733a4bb580412e9cb6f463e9bdf1a": { + "decimals": 18, + "name": "KyberNetwork", + "symbol": "KNC" + }, + "0x9be1001d601102ae0f24ab4764dd5ce2f3e5b096": { + "decimals": 6, + "name": "USD Coin", + "symbol": "USDC" + } + } }, - "0xdd974d5c2e2928dea5f71b9825b8b646686bd200": { - "decimals": 18, - "name": "KyberNetwork", - "symbol": "KNC" - }, - "0x8e870d67f660d95d5be530380d0ec0bd388289e1": { - "decimals": 18, - "name": "Paxos Standard", - "symbol": "PAX" - }, - "0x0000000000085d4780B73119b644AE5ecd22b376": { - "decimals": 18, - "name": "TrueUSD", - "symbol": "TUSD" - }, - "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": { - "decimals": 6, - "name": "USD Coin", - "symbol": "USDC" - }, - "0xdac17f958d2ee523a2206206994597c13d831ec7": { - "decimals": 6, - "name": "Tether USD", - "symbol": "USDT" - }, - "0xCc80C051057B774cD75067Dc48f8987C4Eb97A5e": { - "decimals": 18, - "name": "Nectar", - "symbol": "NEC" + "xdai": { + "baseTokenName": "xDAI", + "genName": "xGEN", + "tokens": {} } } diff --git a/package-lock.json b/package-lock.json index 8f7603f0d..990d9ddd9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1352,6 +1352,11 @@ "integrity": "sha512-4Th98KlMHr5+JkxfcoDT//6vY8vM+iSPrLNpHhRyLx2CFYi8e2RfqPLdpbnpo0Q5lQC5hNB79yes07zb02fvCw==", "dev": true }, + "@burner-wallet/burner-connect-provider": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@burner-wallet/burner-connect-provider/-/burner-connect-provider-0.1.1.tgz", + "integrity": "sha512-cHA2D/Jtn6QRc3szTwSHlJVxz5xKAW6coZw5OK0pwA7IQkZQmbs8DPIAa/qHYKCh7oP2CGcC0V45QgV18cqx4w==" + }, "@cnakazawa/watch": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", @@ -17140,12 +17145,14 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "ansi-styles": { "version": "3.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -17153,7 +17160,8 @@ }, "bindings": { "version": "1.5.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "dev": true, "requires": { "file-uri-to-path": "1.0.0" @@ -17161,7 +17169,8 @@ }, "bip66": { "version": "1.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", + "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -17169,17 +17178,20 @@ }, "bn.js": { "version": "4.11.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", "dev": true }, "brorand": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, "browserify-aes": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { "buffer-xor": "^1.0.3", @@ -17192,22 +17204,26 @@ }, "buffer-from": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, "buffer-xor": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, "camelcase": { "version": "5.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "cipher-base": { "version": "1.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -17216,7 +17232,8 @@ }, "cliui": { "version": "5.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { "string-width": "^3.1.0", @@ -17226,7 +17243,8 @@ }, "color-convert": { "version": "1.9.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { "color-name": "1.1.3" @@ -17234,12 +17252,14 @@ }, "color-name": { "version": "1.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "create-hash": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { "cipher-base": "^1.0.1", @@ -17251,7 +17271,8 @@ }, "create-hmac": { "version": "1.1.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { "cipher-base": "^1.0.3", @@ -17264,7 +17285,8 @@ }, "cross-spawn": { "version": "6.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -17276,12 +17298,14 @@ }, "decamelize": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, "drbg.js": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", + "integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=", "dev": true, "requires": { "browserify-aes": "^1.0.6", @@ -17291,7 +17315,8 @@ }, "elliptic": { "version": "6.5.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.0.tgz", + "integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==", "dev": true, "requires": { "bn.js": "^4.4.0", @@ -17305,12 +17330,14 @@ }, "emoji-regex": { "version": "7.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "end-of-stream": { "version": "1.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { "once": "^1.4.0" @@ -17318,7 +17345,8 @@ }, "ethereumjs-util": { "version": "6.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz", + "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==", "dev": true, "requires": { "bn.js": "^4.11.0", @@ -17332,7 +17360,8 @@ }, "ethjs-util": { "version": "0.1.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", "dev": true, "requires": { "is-hex-prefixed": "1.0.0", @@ -17341,7 +17370,8 @@ }, "evp_bytestokey": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "requires": { "md5.js": "^1.3.4", @@ -17350,7 +17380,8 @@ }, "execa": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "requires": { "cross-spawn": "^6.0.0", @@ -17364,12 +17395,14 @@ }, "file-uri-to-path": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "dev": true }, "find-up": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { "locate-path": "^3.0.0" @@ -17377,12 +17410,14 @@ }, "get-caller-file": { "version": "2.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-stream": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { "pump": "^3.0.0" @@ -17390,7 +17425,8 @@ }, "hash-base": { "version": "3.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -17399,7 +17435,8 @@ }, "hash.js": { "version": "1.1.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -17408,7 +17445,8 @@ }, "hmac-drbg": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { "hash.js": "^1.0.3", @@ -17418,37 +17456,44 @@ }, "inherits": { "version": "2.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "invert-kv": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "is-hex-prefixed": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=", "dev": true }, "is-stream": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, "isexe": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "keccak": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", + "integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==", "dev": true, "requires": { "bindings": "^1.2.1", @@ -17459,7 +17504,8 @@ }, "lcid": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", "dev": true, "requires": { "invert-kv": "^2.0.0" @@ -17467,7 +17513,8 @@ }, "locate-path": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { "p-locate": "^3.0.0", @@ -17476,7 +17523,8 @@ }, "map-age-cleaner": { "version": "0.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "dev": true, "requires": { "p-defer": "^1.0.0" @@ -17484,7 +17532,8 @@ }, "md5.js": { "version": "1.3.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "requires": { "hash-base": "^3.0.0", @@ -17494,7 +17543,8 @@ }, "mem": { "version": "4.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", "dev": true, "requires": { "map-age-cleaner": "^0.1.1", @@ -17504,32 +17554,38 @@ }, "mimic-fn": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, "minimalistic-assert": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true }, "minimalistic-crypto-utils": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", "dev": true }, "nan": { "version": "2.14.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true }, "nice-try": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "npm-run-path": { "version": "2.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { "path-key": "^2.0.0" @@ -17537,7 +17593,8 @@ }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" @@ -17545,7 +17602,8 @@ }, "os-locale": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { "execa": "^1.0.0", @@ -17555,22 +17613,26 @@ }, "p-defer": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", "dev": true }, "p-finally": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, "p-is-promise": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", "dev": true }, "p-limit": { "version": "2.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -17578,7 +17640,8 @@ }, "p-locate": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { "p-limit": "^2.0.0" @@ -17586,22 +17649,26 @@ }, "p-try": { "version": "2.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "path-exists": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "path-key": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "pump": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -17610,17 +17677,20 @@ }, "require-directory": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, "require-main-filename": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "ripemd160": { "version": "2.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, "requires": { "hash-base": "^3.0.0", @@ -17629,7 +17699,8 @@ }, "rlp": { "version": "2.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.3.tgz", + "integrity": "sha512-l6YVrI7+d2vpW6D6rS05x2Xrmq8oW7v3pieZOJKBEdjuTF4Kz/iwk55Zyh1Zaz+KOB2kC8+2jZlp2u9L4tTzCQ==", "dev": true, "requires": { "bn.js": "^4.11.1", @@ -17638,12 +17709,14 @@ }, "safe-buffer": { "version": "5.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", "dev": true }, "secp256k1": { "version": "3.7.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.7.1.tgz", + "integrity": "sha512-1cf8sbnRreXrQFdH6qsg2H71Xw91fCCS9Yp021GnUNJzWJS/py96fS4lHbnTnouLp08Xj6jBoBB6V78Tdbdu5g==", "dev": true, "requires": { "bindings": "^1.5.0", @@ -17658,17 +17731,20 @@ }, "semver": { "version": "5.7.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, "sha.js": { "version": "2.4.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -17677,7 +17753,8 @@ }, "shebang-command": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -17685,22 +17762,26 @@ }, "shebang-regex": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, "source-map": { "version": "0.6.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "source-map-support": { "version": "0.5.12", - "bundled": true, + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", + "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -17709,7 +17790,8 @@ }, "string-width": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -17719,7 +17801,8 @@ }, "strip-ansi": { "version": "5.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -17727,12 +17810,14 @@ }, "strip-eof": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, "strip-hex-prefix": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", "dev": true, "requires": { "is-hex-prefixed": "1.0.0" @@ -17740,7 +17825,8 @@ }, "which": { "version": "1.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -17748,12 +17834,14 @@ }, "which-module": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, "wrap-ansi": { "version": "5.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "requires": { "ansi-styles": "^3.2.0", @@ -17763,17 +17851,20 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "y18n": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, "yargs": { "version": "13.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", "dev": true, "requires": { "cliui": "^5.0.0", @@ -17791,7 +17882,8 @@ }, "yargs-parser": { "version": "13.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -38912,9 +39004,9 @@ } }, "web3connect": { - "version": "1.0.0-beta.23", - "resolved": "https://registry.npmjs.org/web3connect/-/web3connect-1.0.0-beta.23.tgz", - "integrity": "sha512-kjKrojMWjQp9DJEOW51dyjtVDD//6/SuYI7jVLonnkHyLaTxg00aO6Yk1orLcdkGwbxCixtZJ+Ht3+HmVXWUiA==", + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/web3connect/-/web3connect-1.0.0-beta.29.tgz", + "integrity": "sha512-zOPt5BYMIKHN1G87vyOXIlxhYF7zKf0HZNW+uooPue3foM8gEybVeHznTsSB7V5kYN3hTvaq1pyFCVeu4TILeg==", "requires": { "prop-types": "^15.7.2", "react": "^16.8.6", diff --git a/package.json b/package.json index 21a58dc9f..1896e93eb 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "start-docker": "node --max_old_space_size=4096 ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack.docker.config.js", "start-prod": "cross-env NODE_ENV=production node --max_old_space_size=4096 ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack.dev.config.js", "start-staging": "cross-env NODE_ENV=staging node --max_old_space_size=4096 ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack.dev.config.js", + "start-xdai": "cross-env NODE_ENV=xdai node --max_old_space_size=4096 ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack.dev.config.js", "storybook": "start-storybook", "test": "wdio ./test/integration/wdio.conf.js", "test:integration": "wdio ./test/integration/wdio.conf.js --inspect", @@ -74,6 +75,7 @@ }, "dependencies": { "3box": "^1.16.1", + "@burner-wallet/burner-connect-provider": "^0.1.1", "@daostack/client": "0.2.59", "@dorgtech/daocreator-ui-v1": "^0.2.0-rc.20", "@fortawesome/fontawesome-svg-core": "^1.2.10", @@ -136,7 +138,7 @@ "ts-node": "^5.0.1", "utility-types": "^3.7.0", "web3": "1.2.4", - "web3connect": "1.0.0-beta.23", + "web3connect": "^1.0.0-beta.29", "webpack": "^4.36.1", "ws": "^7.1.0" }, diff --git a/src/arc.ts b/src/arc.ts index 31f285bd8..9696ebed7 100644 --- a/src/arc.ts +++ b/src/arc.ts @@ -1,15 +1,11 @@ import { Address, Arc } from "@daostack/client"; -// @ts-ignore -import WalletConnectProvider from "@walletconnect/web3-provider"; import { NotificationStatus } from "reducers/notifications"; import { Observable } from "rxjs"; import Web3Connect from "web3connect"; import { IProviderInfo } from "web3connect/lib/helpers/types"; import { settings } from "./settings"; -import { getNetworkId, getNetworkName, waitUntilTrue, isMobileBrowser } from "./lib/util"; +import { getNetworkId, getNetworkName } from "./lib/util"; -const Portis = require("@portis/web3"); -const Fortmatic = require("fortmatic"); const Web3 = require("web3"); /** @@ -17,96 +13,45 @@ const Web3 = require("web3"); * It is like window.ethereum, but has not necessarily been injected as such. */ let selectedProvider: any; +// eslint-disable-next-line @typescript-eslint/ban-ts-ignore +// @ts-ignore +let web3ConnectCore: Web3Connect.Core; let initializedAccount: Address; -const web3ConnectProviderOptions = - Object.assign({ - }, - (process.env.NODE_ENV === "production") ? - { - network: "mainnet", - walletconnect: { - package: isMobileBrowser() ? null : WalletConnectProvider, - options: { - infuraId: "e0cdf3bfda9b468fa908aa6ab03d5ba2", - }, - }, - portis: { - package: Portis, - options: { - id: "aae9cff5-6e61-4b68-82dc-31a5a46c4a86", - }, - }, - fortmatic: { - package: Fortmatic, - options: { - key: "pk_live_38A2BD2B1D4E9912", - }, - }, - squarelink: { - options: { - id: null, - }, - }, - } - : (process.env.NODE_ENV === "staging") ? - { - network: "rinkeby", - walletconnect: { - package: isMobileBrowser() ? null : WalletConnectProvider, - options: { - infuraId: "e0cdf3bfda9b468fa908aa6ab03d5ba2", - }, - }, - portis: { - package: Portis, - options: { - id: "aae9cff5-6e61-4b68-82dc-31a5a46c4a86", - }, - }, - fortmatic: { - package: Fortmatic, - options: { - key: "pk_test_659B5B486EF199E4", - }, - }, - squarelink: { - options: { - id: null, - }, - }, - } : {}); +type networks = "main"|"rinkeby"|"ganache"|"xdai" -/** - * return the default Arc configuration given the execution environment - */ -export function getArcSettings(): any { - let arcSettings: any; +// get the network id that the current build expects ot connect to +export function targetedNetwork(): networks { switch (process.env.NODE_ENV || "development") { - case "test": { - arcSettings = settings.dev; - break; - } + case "test": + case "ganache": case "development": { - arcSettings = settings.dev; - break; + return "ganache"; } - case "staging" : { - arcSettings = settings.staging; - break; + case "staging" : + case "rinkeby" : { + return "rinkeby"; } + case "mainnet": case "production" : { - arcSettings = settings.production; - break; + return "main"; } + case "xdai": + return "xdai"; default: { throw Error(`Unknown NODE_ENV environment: "${process.env.NODE_ENV}"`); } } +} - // do not subscribe to any of the queries - we do all subscriptions manually - arcSettings.graphqlSubscribeToQueries = false; + +/** + * return the default Arc configuration given the execution environment + */ +export function getArcSettings(): any { + const network = targetedNetwork(); + const arcSettings = settings[network]; return arcSettings; } @@ -157,39 +102,6 @@ async function getProviderNetworkName(provider?: any): Promise { return getNetworkName(networkId); } -/** - * Checks if the web3 provider is set to the required network. - * Does not ensure we have access to the user's account. - * throws an Error if no provider or wrong provider - * @param provider web3Provider - * @return the expected network nameif not correct - */ -async function checkWeb3ProviderIsForNetwork(provider: any): Promise { - let expectedNetworkName; - switch (process.env.NODE_ENV) { - case "development": { - expectedNetworkName = "ganache"; - break; - } - case "staging": { - expectedNetworkName = "rinkeby"; - break; - } - case "production": { - expectedNetworkName = "main"; - break; - } - default: { - const msg = `Unknown NODE_ENV: ${process.env.NODE_ENV}`; - // eslint-disable-next-line no-console - console.error(msg); - throw new Error(msg); - } - } - - const networkName = await getProviderNetworkName(provider); - return (networkName === expectedNetworkName) ? null : expectedNetworkName; -} /** * Returns a IWeb3ProviderInfo when a provider has been selected and is fully available. @@ -259,7 +171,7 @@ export async function initializeArc(provider?: any): Promise { } } catch (reason) { // eslint-disable-next-line no-console - console.error(reason.message); + console.error(reason ? reason.message : "unknown error"); } (window as any).arc = success ? arc : null; @@ -267,26 +179,80 @@ export async function initializeArc(provider?: any): Promise { return success; } + +/** + * Checks if the web3 provider is set to the required network. + * Does not ensure we have access to the user's account. + * throws an Error if no provider or wrong provider + * @param provider web3Provider + * @return the expected network nameif not correct +*/ async function ensureCorrectNetwork(provider: any): Promise { /** * It is required that the provider be the correct one for the current platform */ - const correctNetworkErrorMsg = await checkWeb3ProviderIsForNetwork(provider); + const expectedNetworkName = targetedNetwork(); - if (correctNetworkErrorMsg) { - // eslint-disable-next-line no-console - console.error(`connected to the wrong network, should be ${correctNetworkErrorMsg}`); - throw new Error(`Please connect your wallet provider to ${correctNetworkErrorMsg}`); + // TODO: we should not use the network NAME but the network ID to identify the network... + const networkName = await getProviderNetworkName(provider); + + if (networkName !== expectedNetworkName) { + if (expectedNetworkName === "xdai") { + // TODO: xdai is reporting network 'unknown (100)` , it seems + if (networkName === "unknown (100)") { + // we are fine, mayby + return; + } + } + console.error(`connected to the wrong network, should be ${expectedNetworkName} (instead of "${networkName}")`); + throw new Error(`Please connect your wallet provider to ${expectedNetworkName}`); + } +} + +const ACCOUNT_STORAGEKEY = "currentAddress"; + +export function cacheWeb3Info(account: Address): void { + if (account) { + localStorage.setItem(ACCOUNT_STORAGEKEY, account); + } else { + localStorage.removeItem(ACCOUNT_STORAGEKEY); } } +export function uncacheWeb3Info(accountToo = true): void { + if (accountToo) { + localStorage.removeItem(ACCOUNT_STORAGEKEY); + } + if (web3ConnectCore) { + web3ConnectCore.clearCachedProvider(); + } + /** + * close is not yet a standard, but soon will be. + * Sadly closing the connection is the only way to clear the WalletConnect cache. + * But clearing its cache will ensure that + * the user can rescan a qrcode when changing WalletConnect provider. + */ + if (selectedProvider && selectedProvider.close) { + selectedProvider.close(); // no need to await + } +} + +export function getCachedAccount(): Address | null { + return localStorage.getItem(ACCOUNT_STORAGEKEY); +} + +export interface IEnableWalletProviderParams { + suppressNotifyOnSuccess?: boolean; + showNotification: any; +} + function inTesting(): boolean { if (process.env.NODE_ENV === "development" && navigator.webdriver) { // in test mode, we have an unlocked ganache and we are not using any wallet // eslint-disable-next-line no-console console.log("not using any wallet, because we are in automated test"); - selectedProvider = new Web3(settings.dev.web3Provider); + selectedProvider = new Web3(settings.ganache.web3Provider); return true; } return false; @@ -295,23 +261,23 @@ function inTesting(): boolean { /** * Prompt user to select a web3Provider and enable their account. * Initializes Arc with the newly-selected web3Provider. - * Is a no-op if already done. + * No-op if `selectedProvider` is already set (one can manually go to readonly mode to clear it) * Side-effect is that `selectedProvider` will be set on success. - * @param provider Optional web3 provider, supplied when using a cached provider * @returns Throws exception on error. */ -async function enableWeb3Provider(provider?: any): Promise { - /** - * Already got a selected provider, and is the same as the requested provider? - * We'll replace it if a differen provider has been supplied. - * Otherwise we'll swing with selectedProvider, and this becomes mostly a no-op. - */ - if (selectedProvider && (!provider || (selectedProvider === provider))) { +async function enableWeb3Provider(): Promise { + if (selectedProvider) { return; - } else if (!provider) { + } - const web3Connect = new Web3Connect.Core({ - modal: false, + let provider: any; + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore + let web3Connect: Web3Connect.Core; + + if (!web3ConnectCore) { + web3Connect = new Web3Connect.Core({ + cacheProvider: true, providerOptions: Object.assign( /** * This will hide the web3connect fallback ("Web3") button which currently @@ -320,53 +286,59 @@ async function enableWeb3Provider(provider?: any): Promise { * that don't support the normal specification. Opera is an example of it." */ { disableInjectedProvider: !((window as any).web3 || (window as any).ethereum) }, - web3ConnectProviderOptions) as any, + getArcSettings().web3ConnectProviderOptions) as any, }); - let resolveOnClosePromise: () => void; - let rejectOnClosePromise: (reason?: any) => void; + // eslint-disable-next-line require-atomic-updates + web3ConnectCore = web3Connect; + } else { + web3Connect = web3ConnectCore; + } - const onClosePromise = new Promise( - (resolve: () => void, reject: (reason?: any) => void): any => { - resolveOnClosePromise = resolve; - rejectOnClosePromise = reject; - web3Connect.on("close", (): any => { - return resolve(); - }); - }); + let resolveOnClosePromise: () => void; + let rejectOnClosePromise: (reason?: any) => void; - web3Connect.on("error", (error: Error): any => { - // eslint-disable-next-line no-console - console.error(`web3Connect closed on error: ${error.message}`); - return rejectOnClosePromise(error); + const onClosePromise = new Promise( + (resolve: () => void, reject: (reason?: any) => void): any => { + resolveOnClosePromise = resolve; + rejectOnClosePromise = reject; + web3Connect.on("close", (): any => { + return resolve(); + }); }); - web3Connect.on("connect", (newProvider: any): any => { - provider = newProvider; - /** - * Because we won't receive the "close" event in this case, even though - * the window will have closed - */ - return resolveOnClosePromise(); - }); + web3Connect.on("error", (error: Error): any => { + // eslint-disable-next-line no-console + console.error(`web3Connect closed on error: ${error ? error.message : "cancelled or unknown error"}`); + return rejectOnClosePromise(error); + }); - try { - web3Connect.toggleModal(); - // assuming reject will result in a throw exception caught below - await onClosePromise; + web3Connect.on("connect", (newProvider: any): any => { + provider = newProvider; + /** + * Because we won't receive the "close" event in this case, even though + * the window will have closed + */ + return resolveOnClosePromise(); + }); - } catch (error) { - // eslint-disable-next-line no-console - console.error(`Unable to connect to web3 provider: ${error.message}`); - throw new Error("Unable to connect to web3 provider"); - } + try { + // note this will load from its cache, if present + web3Connect.toggleModal(); + // assuming reject will result in a throw exception caught below + await onClosePromise; - if (!provider) { - // should only be cancelled, errors should have been handled above - // eslint-disable-next-line no-console - console.warn("uncaught error or user cancelled out"); - return; - } + } catch (error) { + // eslint-disable-next-line no-console + console.error(`Unable to connect to web3 provider: ${error ? error.message : "unknown error"}`); + throw new Error("Unable to connect to web3 provider"); + } + + if (!provider) { + // should only be cancelled, errors should have been handled above + // eslint-disable-next-line no-console + console.warn("uncaught error or user cancelled out"); + return; } /** @@ -375,22 +347,22 @@ async function enableWeb3Provider(provider?: any): Promise { await ensureCorrectNetwork(provider); /** - * now ensure that the user has connected to a network and enabled access to the account, - * whatever the provider requires.... - */ + * now ensure that the user has connected to a network and enabled access to the account, + * whatever the provider requires.... + */ try { - // brings up the provider UI as needed + // brings up the provider UI as needed await provider.enable(); // eslint-disable-next-line no-console console.log(`Connected to network provider ${getWeb3ProviderInfo(provider).name}`); } catch (ex) { - // eslint-disable-next-line no-console - console.error(`Unable to enable provider: ${ex.message}`); + // eslint-disable-next-line no-console + console.error(`Unable to enable provider: ${ex.message ? ex : "unknown error"}`); throw new Error("Unable to enable provider"); } if (!await initializeArc(provider)) { - // eslint-disable-next-line no-console + // eslint-disable-next-line no-console console.error("Unable to initialize Arc"); throw new Error("Unable to initialize Arc"); } @@ -416,12 +388,17 @@ async function getCurrentAccountFromProvider(): Promise
{ } /** - * switch to readonly mode (effectively a logout) + * Logout, switch to readonly mode (effectively a logout). + * Clear caches as every case where we're manually logging out + * implies that the cache should be cleared */ -export async function gotoReadonly(showNotification?: any): Promise { +export async function logout(showNotification?: any): Promise { let success = false; + + uncacheWeb3Info(); + if (selectedProvider) { - // clearing this, initializeArc will be made to use the default web3Provider + // clearing this, initializeArc will be made to use the default readonly web3Provider const networkName = await getNetworkName(); // eslint-disable-next-line require-atomic-updates selectedProvider = undefined; @@ -444,69 +421,6 @@ export async function gotoReadonly(showNotification?: any): Promise { return success; } -/** - * Given IWeb3ProviderInfo, get a web3Provider and InitializeArc with it. - * Throws exception on any failure. - * - * This is meant to be used to bypass allowing the user to select a provider in the - * case where we are initializing the app from a previously-cached (selected) provider. - * @param web3ProviderInfo required IWeb3ProviderInfo - */ -async function setWeb3Provider(web3ProviderInfo: IWeb3ProviderInfo): Promise { - let provider: any; - - try { - switch (web3ProviderInfo.type) { - case "injected": - /** - * Safe doesn't always inject itself in a timely manner - */ - if (!(window as any).ethereum) { - await waitUntilTrue((): boolean => !!(window as any).ethereum, 2000); - } - - provider = await Web3Connect.ConnectToInjected(); - break; - case "qrcode": - provider = await Web3Connect.ConnectToWalletConnect( - web3ConnectProviderOptions.walletconnect.package, - Object.assign(web3ConnectProviderOptions.walletconnect.options, { network: web3ConnectProviderOptions.network })); - break; - case "web": - switch (web3ProviderInfo.name) { - case "Portis": - provider = await Web3Connect.ConnectToPortis( - web3ConnectProviderOptions.portis.package, - Object.assign(web3ConnectProviderOptions.portis.options, { network: web3ConnectProviderOptions.network })); - break; - case "Fortmatic": - provider = await Web3Connect.ConnectToFortmatic( - web3ConnectProviderOptions.fortmatic.package, - Object.assign(web3ConnectProviderOptions.fortmatic.options, { network: web3ConnectProviderOptions.network })); - break; - } - break; - } - - if (!provider) { - throw new Error("Uncaught error or user cancelled out"); - } - - } catch (ex) { - // eslint-disable-next-line no-console - console.error(`Unable to instantiate provider: ${ex.message}`); - throw new Error("Unable to instantiate provider"); - } - /** - * make sure the provider is the one we're looking for - */ - if (!provider[web3ProviderInfo.check]) { - throw new Error(`instantiated provider is not the one requested: ${provider.name} != ${web3ProviderInfo.name}`); - } - - await enableWeb3Provider(provider); -} - /** * @returns whether we have a current account */ @@ -517,72 +431,6 @@ export function getAccountIsEnabled(): boolean { return !!getWeb3Provider(); } -const ACCOUNT_STORAGEKEY = "currentAddress"; -const WALLETCONNECT_STORAGEKEY = "walletconnect"; -const PROVIDER_STORAGEKEY = "currentWeb3ProviderInfo"; - -export function cacheWeb3Info(account: Address): void { - if (account) { - localStorage.setItem(ACCOUNT_STORAGEKEY, account); - } else { - localStorage.removeItem(ACCOUNT_STORAGEKEY); - } - const providerInfo = getWeb3ProviderInfo(); - if (providerInfo) { - localStorage.setItem(PROVIDER_STORAGEKEY, JSON.stringify(providerInfo)); - } else { - localStorage.removeItem(PROVIDER_STORAGEKEY); - // hack until fixed by WalletConnect (so after logging out, can rescan the QR code) - localStorage.removeItem(WALLETCONNECT_STORAGEKEY); - } -} - -export function uncacheWeb3Info(): void { - localStorage.removeItem(ACCOUNT_STORAGEKEY); - localStorage.removeItem(PROVIDER_STORAGEKEY); - // hack until fixed by WalletConnect (so after logging out, can rescan the QR code) - localStorage.removeItem(WALLETCONNECT_STORAGEKEY); -} - -export function getCachedAccount(): Address | null { - return localStorage.getItem(ACCOUNT_STORAGEKEY); -} - -export function getCachedWeb3ProviderInfo(): IWeb3ProviderInfo | null { - const cached = localStorage.getItem(PROVIDER_STORAGEKEY); - return cached ? JSON.parse(cached) : null; -} - - -/** - * fully enable a cached provider, if available. Noop is nothing is cached or - * current provider is the same as the given one. - * Exception if cached provider can't be fully enabled. - * @param showNotification - */ -async function loadCachedWeb3Provider(): Promise { - - const cachedWeb3ProviderInfo = getCachedWeb3ProviderInfo(); - if (cachedWeb3ProviderInfo) { - - try { - await setWeb3Provider(cachedWeb3ProviderInfo); - // eslint-disable-next-line no-console - console.log("using cached web3Provider"); - } catch(ex) { - // eslint-disable-next-line no-console - console.error("failed to instantiate cached web3Provider"); - uncacheWeb3Info(); - throw new Error(ex); - } - } -} - -export interface IEnableWalletProviderParams { - suppressNotifyOnSuccess?: boolean; - showNotification: any; -} - /** * Load web3 wallet provider, first trying from cache, otherwise prompting. * This is the only point of contact with the rest of the app for connecting to a wallet. @@ -598,11 +446,7 @@ export async function enableWalletProvider(options: IEnableWalletProviderParams) } if (!selectedProvider) { - await loadCachedWeb3Provider(); - if (!selectedProvider) - { - await enableWeb3Provider(); - } + await enableWeb3Provider(); if (!selectedProvider) { // something went wrong somewhere throw new Error("Unable to connect to a wallet"); @@ -626,17 +470,20 @@ export async function enableWalletProvider(options: IEnableWalletProviderParams) * This will result in completely logging out the user and clearing the cached provider, * thus enabling them to have a choice of providers when they triy to log in again. */ - await gotoReadonly(options.showNotification); + await logout(options.showNotification); throw new Error(ex); } } } catch(err) { let msg: string; - msg = err.message || "Unable to connect to the ethereum provider"; + msg = err ? err.message : "Unable to connect to the ethereum provider"; if (msg.match(/response has no error or result for request/g)) { msg = "Unable to connect to ethereum provider, sorry :-("; } + + uncacheWeb3Info(false); + if (options.showNotification) { options.showNotification(NotificationStatus.Failure, msg); } else { @@ -667,10 +514,11 @@ export function pollForAccountChanges(currentAccountAddress: Address | null, int .then(async (account: Address | null): Promise => { if (prevAccount !== account) { if (account && initializedAccount && (account !== initializedAccount)) { - /** - * handle when user changes account in MetaMask while already connected to Alchemy, thus - * having bypassed `enableWeb3Provider` and not having called `initializeArc`. - */ + /** + * Handle when user changes account in MetaMask while already connected to Alchemy. + * Also handles how the Burner provider switches from a Fortmatic address to the + * burner address at the time of connecting. + */ await initializeArc(selectedProvider); } observer.next(account); @@ -679,10 +527,10 @@ export function pollForAccountChanges(currentAccountAddress: Address | null, int } }) // eslint-disable-next-line no-console - .catch((err): void => {console.error(err.message); }); + .catch((err): void => {console.error(err ? err.message : "unknown error"); }); } catch (ex) { // eslint-disable-next-line no-console - console.error(ex.message); + console.error(ex ? ex.message : "unknown error"); } finally { running = false; diff --git a/src/components/Account/AccountBalances.tsx b/src/components/Account/AccountBalances.tsx index 19a52ff4b..0305a9927 100644 --- a/src/components/Account/AccountBalances.tsx +++ b/src/components/Account/AccountBalances.tsx @@ -1,5 +1,5 @@ import { Address, IDAOState, IMemberState } from "@daostack/client"; -import { ethErrorHandler } from "lib/util"; +import { baseTokenName, ethErrorHandler, genName } from "lib/util"; import BN = require("bn.js"); import AccountBalance from "components/Account/AccountBalance"; @@ -43,10 +43,10 @@ class AccountBalances extends React.Component {

Holdings

- +
- +
diff --git a/src/components/Account/AccountProfilePage.tsx b/src/components/Account/AccountProfilePage.tsx index aafd0d9ce..9acba6e5f 100644 --- a/src/components/Account/AccountProfilePage.tsx +++ b/src/components/Account/AccountProfilePage.tsx @@ -12,7 +12,7 @@ import ThreeboxModal from "components/Shared/ThreeboxModal"; import withSubscription, { ISubscriptionProps } from "components/Shared/withSubscription"; import { Field, Formik, FormikProps } from "formik"; import Analytics from "lib/analytics"; -import { copyToClipboard, ethErrorHandler, formatTokens } from "lib/util"; +import { baseTokenName, copyToClipboard, ethErrorHandler, genName, formatTokens } from "lib/util"; import { Page } from "pages"; import { parse } from "query-string"; import * as React from "react"; @@ -290,8 +290,8 @@ class AccountProfilePage extends React.Component { {accountInfo ?
Rep. Score
: ""} -
GEN:
{formatTokens(genBalance)}
- -
ETH:
{formatTokens(ethBalance)}
+
{genName()}:
{formatTokens(genBalance)}
+ -
{baseTokenName()}:
{formatTokens(ethBalance)}
ETH Address:
diff --git a/src/components/Daos/DaosPage.tsx b/src/components/Daos/DaosPage.tsx index ccf7f679e..bfc4384ca 100644 --- a/src/components/Daos/DaosPage.tsx +++ b/src/components/Daos/DaosPage.tsx @@ -29,10 +29,12 @@ class DaosPage extends React.Component { let daos: DAO[]; if (process.env.NODE_ENV === "staging") { // on staging we show all daos (registered or not) - daos = data.filter((d: DAO) => d.staticState.name === "Genesis Alpha") + daos = data + .filter((d: DAO) => d.staticState.name === "Genesis Alpha") .concat(data.filter((d: DAO) => d.staticState.name !== "Genesis Alpha")); } else { - daos = data.filter((d: DAO) => d.staticState.name === "Genesis Alpha") + daos = data + .filter((d: DAO) => d.staticState.name === "Genesis Alpha") .concat(data.filter((d: DAO) => d.staticState.name !== "Genesis Alpha" && d.staticState.register === "registered")); } diff --git a/src/components/Proposal/Create/SchemeForms/CreateContributionRewardProposal.tsx b/src/components/Proposal/Create/SchemeForms/CreateContributionRewardProposal.tsx index 87bf1e275..ebddfe2f6 100644 --- a/src/components/Proposal/Create/SchemeForms/CreateContributionRewardProposal.tsx +++ b/src/components/Proposal/Create/SchemeForms/CreateContributionRewardProposal.tsx @@ -9,7 +9,7 @@ import UserSearchField from "components/Shared/UserSearchField"; import TagsSelector from "components/Proposal/Create/SchemeForms/TagsSelector"; import TrainingTooltip from "components/Shared/TrainingTooltip"; import Analytics from "lib/analytics"; -import { supportedTokens, toBaseUnit, tokenDetails, toWei, isValidUrl } from "lib/util"; +import { baseTokenName, supportedTokens, toBaseUnit, tokenDetails, toWei, isValidUrl } from "lib/util"; import { showNotification, NotificationStatus } from "reducers/notifications"; import { exportUrl, importUrlValues } from "lib/proposalUtils"; import * as css from "../CreateProposal.scss"; @@ -311,12 +311,12 @@ class CreateContributionReward extends React.Component {
{
{ if (contributionReward && currentAccountAddress === contributionReward.beneficiary) { const rewards = getCRRewards(proposal); if (rewards.ethReward) { - rewardComponents.push(formatTokens(rewards.ethReward, "ETH")); + rewardComponents.push(formatTokens(rewards.ethReward, baseTokenName())); } if (rewards.externalTokenReward) { rewardComponents.push(formatTokens(rewards.externalTokenReward, tokenSymbol(contributionReward.externalToken), tokenDecimals(contributionReward.externalToken))); @@ -56,7 +56,7 @@ export default class RedemptionsString extends React.Component { } if (gen.gt(zero)) { - rewardComponents.push(formatTokens(gen, "GEN")); + rewardComponents.push(formatTokens(gen, genName())); } if (reputation.gt(zero)) { diff --git a/src/components/Proposal/RedemptionsTip.tsx b/src/components/Proposal/RedemptionsTip.tsx index c2141bed7..f2d1621a6 100644 --- a/src/components/Proposal/RedemptionsTip.tsx +++ b/src/components/Proposal/RedemptionsTip.tsx @@ -1,6 +1,6 @@ import { Address, IDAOState, IProposalState, IProposalOutcome } from "@daostack/client"; import Reputation from "components/Account/Reputation"; -import { formatTokens, fromWei, tokenDecimals, tokenSymbol, AccountClaimableRewardsType } from "lib/util"; +import { baseTokenName, formatTokens, fromWei, genName, tokenDecimals, tokenSymbol, AccountClaimableRewardsType } from "lib/util"; import * as React from "react"; import * as css from "components/Shared/PreTransactionModal.scss"; @@ -49,7 +49,7 @@ export default (props: IProps) => { c =
For staking on the proposal you are due to receive:
    -
  • {fromWei(gpRewards.tokensForStaker)} GEN
  • +
  • {fromWei(gpRewards.tokensForStaker)} {genName()}
; rewardComponents.push(c); @@ -58,7 +58,7 @@ export default (props: IProps) => { c =
For staking on the proposal you are due to receive:
    -
  • {fromWei(gpRewards.daoBountyForStaker)} GEN as bounty from {dao.name} +
  • {fromWei(gpRewards.daoBountyForStaker)} {genName()} as bounty from {dao.name}
; @@ -79,7 +79,7 @@ export default (props: IProps) => {
    {contributionRewards["eth"] ?
  • - {formatTokens(contributionReward.ethReward, "ETH")} + {formatTokens(contributionReward.ethReward, baseTokenName())}
  • : "" } {contributionRewards["externalToken"] ? diff --git a/src/components/Proposal/RewardsString.tsx b/src/components/Proposal/RewardsString.tsx index be5cd7323..db4965053 100644 --- a/src/components/Proposal/RewardsString.tsx +++ b/src/components/Proposal/RewardsString.tsx @@ -2,7 +2,7 @@ import BN = require("bn.js"); import * as React from "react"; import { IDAOState, IProposalState } from "@daostack/client"; -import { formatTokens, tokenDetails } from "lib/util"; +import { baseTokenName, formatTokens, tokenDetails } from "lib/util"; import Reputation from "components/Account/Reputation"; @@ -19,7 +19,7 @@ export default class RewardsString extends React.Component { const contributionReward = proposal.contributionReward; const rewards = []; if (contributionReward.ethReward && contributionReward.ethReward.gt(new BN(0))) { - rewards.push(formatTokens(contributionReward.ethReward, "ETH")); + rewards.push(formatTokens(contributionReward.ethReward, baseTokenName())); } if (contributionReward.externalToken && contributionReward.externalTokenReward && contributionReward.externalTokenReward.gt(new BN(0))) { const tokenData = tokenDetails(contributionReward.externalToken); diff --git a/src/components/Redemptions/RedemptionsPage.tsx b/src/components/Redemptions/RedemptionsPage.tsx index 33ef56dbf..957dca59b 100644 --- a/src/components/Redemptions/RedemptionsPage.tsx +++ b/src/components/Redemptions/RedemptionsPage.tsx @@ -7,7 +7,7 @@ import Loading from "components/Shared/Loading"; import withSubscription, { ISubscriptionProps } from "components/Shared/withSubscription"; import gql from "graphql-tag"; import Analytics from "lib/analytics"; -import { formatTokens, tokenDecimals, tokenSymbol } from "lib/util"; +import { baseTokenName, formatTokens, genName, tokenDecimals, tokenSymbol } from "lib/util"; import { Page } from "pages"; import * as React from "react"; import { BreadcrumbsItem } from "react-breadcrumbs-dynamic"; @@ -200,10 +200,10 @@ class RedemptionsPage extends React.Component { const totalRewards: any[] = []; if (!ethReward.isZero()) { - totalRewards.push(formatTokens(ethReward, "ETH")); + totalRewards.push(formatTokens(ethReward, baseTokenName())); } if (!genReward.isZero()) { - totalRewards.push(formatTokens(genReward, "GEN")); + totalRewards.push(formatTokens(genReward, genName())); } Object.keys(externalTokenRewards).forEach((tokenAddress) => { totalRewards.push(formatTokens(externalTokenRewards[tokenAddress], tokenSymbol(tokenAddress), tokenDecimals(tokenAddress))); diff --git a/src/components/Scheme/ContributionRewardExtRewarders/Competition/Card.tsx b/src/components/Scheme/ContributionRewardExtRewarders/Competition/Card.tsx index 1a435751f..791d216ae 100644 --- a/src/components/Scheme/ContributionRewardExtRewarders/Competition/Card.tsx +++ b/src/components/Scheme/ContributionRewardExtRewarders/Competition/Card.tsx @@ -129,7 +129,7 @@ export default withSubscription({ checkForUpdate: [], createObservable: (props: IExternalProps) => { /** - * Would be better to prime for all cards + * Data will come from the cache created in List */ return combineLatest( getProposalSubmissions(props.proposalState.id, true), diff --git a/src/components/Scheme/ContributionRewardExtRewarders/Competition/Competitions.scss b/src/components/Scheme/ContributionRewardExtRewarders/Competition/Competitions.scss index 53e48985c..f2fe7aa46 100644 --- a/src/components/Scheme/ContributionRewardExtRewarders/Competition/Competitions.scss +++ b/src/components/Scheme/ContributionRewardExtRewarders/Competition/Competitions.scss @@ -412,10 +412,11 @@ a.blueButton { margin-bottom: 20px; .heading { - font-weight: 500; - font-size: 20px; + font-size: 23px; + color: #689bd6; width: 100%; - // text-align: center; + border-bottom: 1px solid #bfcbd5; + padding-bottom: 5px; margin-bottom: 18px; } @@ -441,16 +442,6 @@ a.blueButton { border-bottom: 1px solid #0076ff; } - &.title { - border-top-left-radius: 5px; - border-bottom-left-radius: 5px; - border-left: 1px solid transparent; - - &.selected { - border-left: 1px solid #0076ff; - } - } - &.winnerIcon { padding-top: 0; padding-bottom: 0; @@ -461,6 +452,16 @@ a.blueButton { } } + &.title { + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + border-left: 1px solid transparent; + + &.selected { + border-left: 1px solid #0076ff; + } + } + &.creator { white-space: nowrap; padding-right: 10px; @@ -498,7 +499,7 @@ a.blueButton { } &.overWithWinners { - grid-template-columns: 1fr 10fr 2fr 1fr; + grid-template-columns: 1fr 25fr 2fr 1fr; .cell { &.winnerIcon { @@ -526,17 +527,37 @@ a.blueButton { } .noWinners { - text-align: center; - font-size: 21px; + margin-bottom: 24px; .caption { + font-size: 23px; + width: 100%; + border-bottom: 1px solid #bfcbd5; + padding-bottom: 5px; + color: #689bd6; margin-bottom: 20px; } .body { + color: $black; display: block; } } + + .discussionContainer { + .title { + font-size: 23px; + color: #689bd6; + width: 100%; + border-bottom: 1px solid #bfcbd5; + padding-bottom: 5px; + margin-bottom: 16px; + } + } + + .disqus { + padding: 15px; + } } .createSubmissionForm { @@ -739,7 +760,7 @@ a.blueButton { box-shadow: $shadow-3; max-width: 445px; min-width: 250px; - // the follinwg enable scrolling to work in the description div + // the following enables scrolling to work in the description div max-height: calc(100vh - 136px); flex-direction: column; display: flex; @@ -795,7 +816,7 @@ a.blueButton { padding-top: 5px; // correct for diff in icons padding-bottom: 5px; // correct for diff in icons img { - margin-right: 7px; + margin-right: 5px; } } } @@ -822,7 +843,7 @@ a.blueButton { .createdOn { margin-top: 16px; - margin-bottom: 16px; + // margin-bottom: 16px; font-size: 13px; .datetime { @@ -869,20 +890,20 @@ a.blueButton { margin-top: 16px; } - .discussionContainer { - .title { - font-size: 18px; - color: #689bd6; - width: 100%; - border-bottom: 1px solid #bfcbd5; - padding-bottom: 5px; - margin-bottom: 16px; - } - - .disqus { - padding: 15px; - } - } + // .discussionContainer { + // .title { + // font-size: 18px; + // color: #689bd6; + // width: 100%; + // border-bottom: 1px solid #bfcbd5; + // padding-bottom: 5px; + // margin-bottom: 16px; + // } + + // .disqus { + // padding: 15px; + // } + // } } @media only screen and (max-width: 425px) { @@ -1010,7 +1031,6 @@ a.blueButton { } &.overWithWinners { - // grid-template-columns: 1fr 10fr 2fr 1fr; .cell { &.winnerIcon { border-bottom-left-radius: unset; diff --git a/src/components/Scheme/ContributionRewardExtRewarders/Competition/CreateProposal.tsx b/src/components/Scheme/ContributionRewardExtRewarders/Competition/CreateProposal.tsx index 9d80ad01e..ce072a6b7 100644 --- a/src/components/Scheme/ContributionRewardExtRewarders/Competition/CreateProposal.tsx +++ b/src/components/Scheme/ContributionRewardExtRewarders/Competition/CreateProposal.tsx @@ -3,7 +3,7 @@ import * as arcActions from "actions/arcActions"; import { enableWalletProvider, getArc } from "arc"; import withSubscription, { ISubscriptionProps } from "components/Shared/withSubscription"; import { ErrorMessage, Field, Form, Formik, FormikProps } from "formik"; -import { supportedTokens, toBaseUnit, tokenDetails, toWei, isValidUrl, getLocalTimezone } from "lib/util"; +import { baseTokenName, supportedTokens, toBaseUnit, tokenDetails, toWei, isValidUrl, getLocalTimezone } from "lib/util"; import * as React from "react"; import { connect } from "react-redux"; import Select from "react-select"; @@ -81,6 +81,7 @@ const CustomDateInput: React.SFC = ({ field, form }) => { form.setFieldValue(field.name, date); return true; }; + return { const votingStartTimeInput = values.votingStartTimeInput; const suggestionEndTimeInput = values.suggestionEndTimeInput; - if (compStartTimeInput && compStartTimeInput.isSameOrBefore(now)) { - errors.compStartTimeInput = "Competition must start in the future"; + if (!(compStartTimeInput instanceof moment)) { + errors.compStartTimeInput = "Invalid datetime format"; + } else { + if (compStartTimeInput && compStartTimeInput.isSameOrBefore(now)) { + errors.compStartTimeInput = "Competition must start in the future"; + } } - if (suggestionEndTimeInput && (suggestionEndTimeInput.isSameOrBefore(compStartTimeInput))) { - errors.suggestionEndTimeInput = "Submission period must end after competition starts"; + if (!(suggestionEndTimeInput instanceof moment)) { + errors.suggestionEndTimeInput = "Invalid datetime format"; + } else { + if (suggestionEndTimeInput && (suggestionEndTimeInput.isSameOrBefore(compStartTimeInput))) { + errors.suggestionEndTimeInput = "Submission period must end after competition starts"; + } } - if (votingStartTimeInput && suggestionEndTimeInput && (votingStartTimeInput.isBefore(suggestionEndTimeInput))) { - errors.votingStartTimeInput = "Voting must start on or after submission period ends"; + if (!(votingStartTimeInput instanceof moment)) { + errors.votingStartTimeInput = "Invalid datetime format"; + } else { + if (votingStartTimeInput && suggestionEndTimeInput && (votingStartTimeInput.isBefore(suggestionEndTimeInput))) { + errors.votingStartTimeInput = "Voting must start on or after submission period ends"; + } } - if (compEndTimeInput && compEndTimeInput.isSameOrBefore(votingStartTimeInput)) { - errors.compEndTimeInput = "Competion must end after voting starts"; + if (!(compEndTimeInput instanceof moment)) { + errors.compEndTimeInput = "Invalid datetime format"; + } else { + if (compEndTimeInput && compEndTimeInput.isSameOrBefore(votingStartTimeInput)) { + errors.compEndTimeInput = "Competion must end after voting starts"; + } } if (!isValidUrl(values.url)) { @@ -454,12 +471,12 @@ class CreateProposal extends React.Component {
    { }; } + private disqusConfig = { url: "", identifier: "", title: "" }; + private getCompetitionState = (): CompetitionStatus => { const competition = this.props.proposalState.competition; const submissions = this.props.data[0]; @@ -221,7 +223,7 @@ class CompetitionDetails extends React.Component {
    - { } + { formatTokens(submission.totalVotes) } Rep{/**/ }
    @@ -249,7 +251,16 @@ class CompetitionDetails extends React.Component { const voting = status.voting; const isOver = status.over; const overWithWinners = status.overWithWinners; - const canSubmit = inSubmissions && proposalState.executedAt; + const submissionsAreDisabled = notStarted || + // note that winningOutcome1 is the *current* state, not necessarily the *final* outcome + (!proposalState.executedAt || (proposalState.winningOutcome !== IProposalOutcome.Pass)) + // FAKE: restore after .admin is made available + // || (proposalState.admin && (this.props.currentAccountAddress !== proposalState.admin)) + ; + + this.disqusConfig.title = proposalState.title; + this.disqusConfig.url = process.env.BASE_URL + this.props.history.location.pathname; + this.disqusConfig.identifier = `competition-${proposalState.id}`; return {schemeName(proposalState.scheme, proposalState.scheme.address)} @@ -261,16 +272,27 @@ class CompetitionDetails extends React.Component {
    Go to Proposal >
    -
    - { canSubmit ? - + New Submission - : "" - } -
    + { status.now.isBefore(status.competition.suggestionsEndTime) ? +
    + { + + + New Submission + + } +
    + : "" + }
    {humanProposalTitle(proposalState)}
    @@ -340,6 +362,14 @@ class CompetitionDetails extends React.Component { } { ((inVoting && !voting) || (isOver && !overWithWinners)) ? this.noWinnersHtml() : "" } + +
    +
    Discussion
    +
    + +
    +
    +
    {this.state.showingCreateSubmission ? @@ -356,6 +386,10 @@ class CompetitionDetails extends React.Component {
    { props.error.message }
    , checkForUpdate: ["currentAccountAddress"], - createObservable: (props: IExternalProps & IExternalStateProps ) => { + createObservable: async (props: IExternalProps & IExternalStateProps ) => { + await primeCacheForSubmissionsAndVotes(); /** * We subscribe here because we can't rely on arriving at * this component via CompetitionCard, and thus must create our own subscription. diff --git a/src/components/Scheme/ContributionRewardExtRewarders/Competition/List.tsx b/src/components/Scheme/ContributionRewardExtRewarders/Competition/List.tsx index 669be504d..2300e3330 100644 --- a/src/components/Scheme/ContributionRewardExtRewarders/Competition/List.tsx +++ b/src/components/Scheme/ContributionRewardExtRewarders/Competition/List.tsx @@ -2,7 +2,8 @@ import * as React from "react"; import { BreadcrumbsItem } from "react-breadcrumbs-dynamic"; import { ISchemeState, IDAOState, IProposalState } from "@daostack/client"; import { SortService } from "lib/sortService"; -import { CompetitionStatusEnum, CompetitionStatus } from "./utils"; +import withSubscription, { ISubscriptionProps } from "components/Shared/withSubscription"; +import { CompetitionStatusEnum, CompetitionStatus, primeCacheForSubmissionsAndVotes } from "./utils"; import Card from "./Card"; import * as css from "./Competitions.scss"; @@ -16,9 +17,9 @@ interface IStateProps { statusMap: Map; } -type IProps = IExternalProps; +type IProps = IExternalProps & ISubscriptionProps; -export default class CompetitionsList extends React.Component { +class CompetitionsList extends React.Component { constructor(props: IProps) { super(props); @@ -97,3 +98,14 @@ export default class CompetitionsList extends React.Component; } } + +export default withSubscription({ + wrappedComponent: CompetitionsList, + loadingComponent: null, + errorComponent: (props) =>
    { props.error.message }
    , + checkForUpdate: [], + createObservable: async (_props: IExternalProps) => { + return primeCacheForSubmissionsAndVotes(); + }, +}); + diff --git a/src/components/Scheme/ContributionRewardExtRewarders/Competition/SubmissionDetails.tsx b/src/components/Scheme/ContributionRewardExtRewarders/Competition/SubmissionDetails.tsx index a62403fa2..5f95eec9b 100644 --- a/src/components/Scheme/ContributionRewardExtRewarders/Competition/SubmissionDetails.tsx +++ b/src/components/Scheme/ContributionRewardExtRewarders/Competition/SubmissionDetails.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import withSubscription, { ISubscriptionProps } from "components/Shared/withSubscription"; -import { ensureHttps, formatFriendlyDateForLocalTimezone} from "lib/util"; +import { ensureHttps, formatFriendlyDateForLocalTimezone, formatTokens} from "lib/util"; import { IRootState } from "reducers"; import { connect } from "react-redux"; import classNames from "classnames"; @@ -13,9 +13,9 @@ import { IProfilesState } from "reducers/profilesReducer"; import { combineLatest, of } from "rxjs"; import Tooltip from "rc-tooltip"; import TagsSelector from "components/Proposal/Create/SchemeForms/TagsSelector"; -import Reputation from "components/Account/Reputation"; import { DiscussionEmbed } from "disqus-react"; -import { getProposalSubmission, getSubmissionVoterHasVoted, getCompetitionVotes, CompetitionStatus } from "./utils"; +import { RouteComponentProps } from "react-router-dom"; +import { getSubmission, getSubmissionVoterHasVoted, getCompetitionVotes, CompetitionStatus } from "./utils"; import * as css from "./Competitions.scss"; const ReactMarkdown = require("react-markdown"); @@ -26,7 +26,7 @@ interface IExternalStateProps { profiles: IProfilesState; } -interface IExternalProps { +interface IExternalProps extends RouteComponentProps { currentAccountAddress: Address; daoState: IDAOState; proposalState: IProposalState; @@ -61,7 +61,6 @@ class SubmissionDetails extends React.Component { public render(): RenderOutput { const competition = this.props.proposalState.competition; - const daoState = this.props.daoState; const submission = this.props.data[0]; const currentAccountVotedForIt = this.props.data[1]; const currentAccountVotes = this.props.data[2]; @@ -77,7 +76,7 @@ class SubmissionDetails extends React.Component { const tags = submission.tags; this.disqusConfig.title = submission.title; - this.disqusConfig.url = window.location.toString(); + this.disqusConfig.url = process.env.BASE_URL + this.props.history.location.pathname; this.disqusConfig.identifier = submission.id; return ( @@ -86,7 +85,7 @@ class SubmissionDetails extends React.Component {
    - { } + { formatTokens(submission.totalVotes) } Rep { /* */}
    { (canRedeem || !competitionIsOver) ?
    @@ -148,12 +147,16 @@ class SubmissionDetails extends React.Component {
    Created:
    {formatFriendlyDateForLocalTimezone(competition.createdAt)}
    -
    -
    Discussion
    -
    - + { + // eslint-disable-next-line no-constant-condition + (false) ?
    +
    Discussion
    +
    + +
    -
    + : "" + }
    ); @@ -165,9 +168,12 @@ const SubmissionDetailsSubscription = withSubscription({ loadingComponent: null, errorComponent: (props) =>
    { props.error.message }
    , checkForUpdate: ["currentAccountAddress"], - createObservable: async (props: IExternalProps) => { + createObservable: (props: IExternalProps) => { + /** + * data comes from the cache created in Details + */ return combineLatest( - getProposalSubmission(props.proposalState.id, props.suggestionId, true), + getSubmission(props.suggestionId, true), getSubmissionVoterHasVoted(props.suggestionId, props.currentAccountAddress, true), props.currentAccountAddress ? getCompetitionVotes(props.proposalState.id, props.currentAccountAddress, true) : of([]) ); diff --git a/src/components/Scheme/ContributionRewardExtRewarders/Competition/utils.ts b/src/components/Scheme/ContributionRewardExtRewarders/Competition/utils.ts index 1e0d885d0..f3111ea51 100644 --- a/src/components/Scheme/ContributionRewardExtRewarders/Competition/utils.ts +++ b/src/components/Scheme/ContributionRewardExtRewarders/Competition/utils.ts @@ -6,7 +6,7 @@ import moment = require("moment"); import { getArc } from "arc"; import { operationNotifierObserver } from "actions/arcActions"; import { IRootState } from "reducers"; -import { Observable, of } from "rxjs"; +import { Observable, of, combineLatest } from "rxjs"; import { map, mergeMap, toArray, first } from "rxjs/operators"; /** @@ -160,49 +160,33 @@ export const redeemForSubmission = (options: IVoteSubmissionOptions ): ThunkActi }; }; - -/** - * must be an exact subset of ICompetitionSuggestionQueryOptions - */ -export interface IGetSubmissionsOptions { - id?: string; // id of the competition - suggestionId?: number; // the "suggestionId" is a counter that is unique to the scheme - // - and is not to be confused with suggestion.id -} - - -const getSubmissions = ( - proposalId: string, - options?: IGetSubmissionsOptions, - subscribe = false -): Observable> => { - const competition = new Competition(proposalId, getArc()); +export const getProposalSubmissions = (proposalId: string, subscribe = false): Observable> => { // fetchAllData so .state() comes from cache - return competition.suggestions({ where: options }, { subscribe, fetchAllData: true }) + const competition = new Competition(proposalId, getArc()); + return competition.suggestions(undefined, { subscribe, fetchAllData: true }) .pipe( mergeMap(submissions => of(submissions).pipe( mergeMap(submissions => submissions), mergeMap((submission: CompetitionSuggestion) => submission.state().pipe(first())), toArray() - )) - ); -}; - -export const getProposalSubmissions = (proposalId: string, subscribe = false): Observable> => { - return getSubmissions(proposalId, undefined, subscribe); + ))); }; -export const getProposalSubmission = (proposalId: string, id: string, subscribe = false): Observable => { - return getSubmissions(proposalId, { id }, subscribe).pipe( - map((suggestions: Array) => suggestions.length ? suggestions[0]: null )); +export const getSubmission = (id: string, subscribe = false): Observable => { + const submission = new CompetitionSuggestion(id, getArc()); + return submission.state({ subscribe }); }; export const getCompetitionVotes = (competitionId: string, voterAddress?: Address, subscribe = false): Observable> => { - const options = Object.assign({ proposal: competitionId }, voterAddress ? { voter: voterAddress } : {} ); - return CompetitionVote.search(getArc(), { where: options}, { subscribe: subscribe, fetchAllData: false }); + const competition = new Competition(competitionId, getArc()); + /** + * none of the current uses require the vote state + */ + return competition.votes({ where: voterAddress ? { voter: voterAddress } : {}}, + { subscribe: subscribe, fetchAllData: true }); }; -export const getSubmissionVotes = (submissionId: string, voterAddress?: Address, subscribe = false): Observable> => { +const getSubmissionVotes = (submissionId: string, voterAddress?: Address, subscribe = false): Observable> => { // submissionId is the actual id, not the count const submission = new CompetitionSuggestion(submissionId, getArc()); return submission.votes(voterAddress ? { where: { voter: voterAddress } } : {}, { subscribe, fetchAllData: true }); @@ -216,3 +200,10 @@ export const getSubmissionVoterHasVoted = (submissionId: string, voterAddress: s return getSubmissionVotes(submissionId, voterAddress, subscribe) .pipe(map((votes: Array) => !!votes.length)); }; + +export const primeCacheForSubmissionsAndVotes = (): Observable => { + return combineLatest( + CompetitionSuggestion.search(getArc(), {}, { subscribe: true, fetchAllData: true }), + CompetitionVote.search(getArc(), {}, { subscribe: true, fetchAllData: true }) + ); +}; diff --git a/src/layouts/AppContainer.tsx b/src/layouts/AppContainer.tsx index 475af5912..5e2cf66ae 100644 --- a/src/layouts/AppContainer.tsx +++ b/src/layouts/AppContainer.tsx @@ -23,7 +23,7 @@ import { matchPath,Link, Route, RouteComponentProps, Switch } from "react-router import { ModalContainer } from "react-router-modal"; import { IRootState } from "reducers"; import { dismissNotification, INotificationsState, NotificationStatus, showNotification, INotification } from "reducers/notifications"; -import { getCachedAccount, cacheWeb3Info, uncacheWeb3Info, gotoReadonly, pollForAccountChanges } from "arc"; +import { getCachedAccount, cacheWeb3Info, logout, pollForAccountChanges } from "arc"; import ErrorUncaught from "components/Errors/ErrorUncaught"; import { sortedNotifications } from "../selectors/notifications"; import * as css from "./App.scss"; @@ -137,8 +137,7 @@ class AppContainer extends React.Component { if (newAddress) { cacheWeb3Info(newAddress); } else { - uncacheWeb3Info(); - gotoReadonly(this.props.showNotification); + logout(this.props.showNotification); // TODO: save the threebox for each profile separately so we dont have to logout here this.props.threeBoxLogout(); diff --git a/src/layouts/Header.tsx b/src/layouts/Header.tsx index 3ee42adeb..7724b9134 100644 --- a/src/layouts/Header.tsx +++ b/src/layouts/Header.tsx @@ -1,7 +1,7 @@ import { Address, IDAOState } from "@daostack/client"; import * as uiActions from "actions/uiActions"; import { threeBoxLogout } from "actions/profilesActions"; -import { enableWalletProvider, getAccountIsEnabled, getArc, gotoReadonly, getWeb3ProviderInfo } from "arc"; +import { enableWalletProvider, getAccountIsEnabled, getArc, logout, getWeb3ProviderInfo } from "arc"; import classNames from "classnames"; import AccountBalances from "components/Account/AccountBalances"; import AccountImage from "components/Account/AccountImage"; @@ -118,7 +118,7 @@ class Header extends React.Component { } public handleClickLogout = async (_event: any): Promise => { - await gotoReadonly(this.props.showNotification); + await logout(this.props.showNotification); await this.props.threeBoxLogout(); } diff --git a/src/layouts/SidebarMenu.tsx b/src/layouts/SidebarMenu.tsx index cb9327c6f..24edd5256 100644 --- a/src/layouts/SidebarMenu.tsx +++ b/src/layouts/SidebarMenu.tsx @@ -11,7 +11,7 @@ import FollowButton from "components/Shared/FollowButton"; import withSubscription, { ISubscriptionProps } from "components/Shared/withSubscription"; import { generate } from "geopattern"; import Analytics from "lib/analytics"; -import { ethErrorHandler, formatTokens, getExchangesList, supportedTokens } from "lib/util"; +import { baseTokenName, ethErrorHandler, formatTokens, genName, getExchangesList, supportedTokens } from "lib/util"; import { parse } from "query-string"; import * as React from "react"; import { matchPath, Link, RouteComponentProps } from "react-router-dom"; @@ -267,12 +267,12 @@ interface IEthProps extends ISubscriptionProps { const ETHBalance = (props: IEthProps) => { const { data } = props; - return
  • {formatTokens(data)} ETH
  • ; + return
  • {formatTokens(data)} {baseTokenName()}
  • ; }; const SubscribedEthBalance = withSubscription({ wrappedComponent: ETHBalance, - loadingComponent:
  • ... ETH
  • , + loadingComponent:
  • ... {baseTokenName()}
  • , errorComponent: null, checkForUpdate: (oldProps: IEthProps, newProps: IEthProps) => { return oldProps.dao.address !== newProps.dao.address; @@ -292,7 +292,7 @@ const TokenBalance = (props: ITokenProps) => { const { data, error, isLoading, tokenAddress } = props; const tokenData = supportedTokens()[tokenAddress]; - if (isLoading || error || ((data === null || isNaN(data) || data.isZero()) && tokenData.symbol !== "GEN")) { + if (isLoading || error || ((data === null || isNaN(data) || data.isZero()) && tokenData.symbol !== genName())) { return null; } diff --git a/src/lib/util.ts b/src/lib/util.ts index 88e2b54da..d3c7e6b4d 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -1,4 +1,5 @@ import { promisify } from "util"; +import { targetedNetwork } from "arc"; import { Address, IContractInfo, @@ -133,12 +134,20 @@ export function toWei(amount: number): BN { return new BN(getArc().web3.utils.toWei(amount.toFixed(18).toString(), "ether")); } +export function baseTokenName() { + return tokens[targetedNetwork()]["baseTokenName"]; +} + +export function genName() { + return tokens[targetedNetwork()]["genName"]; +} + export function supportedTokens() { return { [getArc().GENToken().address]: { decimals: 18, name: "DAOstack GEN", - symbol: "GEN", - }, ...tokens}; + symbol: genName(), + }, ...tokens[targetedNetwork()]["tokens"]}; } export function formatTokens(amountWei: BN|null, symbol?: string, decimals = 18): string { @@ -380,6 +389,9 @@ export async function getNetworkName(id?: string): Promise { case "rinkeby": case "4": return "rinkeby"; + case "xdai": + case "100": + return "xdai"; case "kovan": case "42": return "kovan"; @@ -522,13 +534,6 @@ export function isValidUrl(str: string, emptyOk = true): boolean { return (emptyOk && (!str || !str.trim())) || (str && pattern.test(str)); } -export function isMobileBrowser(): boolean { - let check = false; - // from here: http://detectmobilebrowsers.com/ - // eslint-disable-next-line no-useless-escape - (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true; })(navigator.userAgent||navigator.vendor||(window as any).opera); - return check; -} export enum GetSchemeIsActiveActions { @@ -623,14 +628,14 @@ export function arrayRemove(arr: any[], value: any) { const localTimezone = moment.tz.guess(); export function getDateWithTimezone(date: Date|moment.Moment): moment.Moment { - return moment.tz(date.toISOString(), localTimezone); + return moment.tz(date.toISOString(), localTimezone); } const tzFormat = "z (Z)"; const dateFormat = `MMM DD, YYYY HH:mm ${tzFormat}`; /** * looks like: "17:30 EST (-05:00) Dec 31, 2019" - * @param date + * @param date */ export function formatFriendlyDateForLocalTimezone(date: Date|moment.Moment): string { return getDateWithTimezone(date).format(dateFormat); diff --git a/src/settings.ts b/src/settings.ts index c5763a9a9..8ec598aee 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,27 +1,142 @@ +import BurnerConnectProvider from "@burner-wallet/burner-connect-provider"; + +const WalletConnectProvider = require("@walletconnect/web3-provider"); +const Portis = require("@portis/web3"); +const Fortmatic = require("fortmatic"); + +function isMobileBrowser(): boolean { + // if (!window) { + // return false + // } + let check = false; + // from here: https://detectmobilebrowsers.com/ + // eslint-disable-next-line no-useless-escape + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true; })(navigator.userAgent||navigator.vendor||(window as any).opera); + return check; +} + export const settings = { - dev: { + ganache: { graphqlHttpProvider: "http://127.0.0.1:8000/subgraphs/name/daostack", graphqlWsProvider: "ws://127.0.0.1:8001/subgraphs/name/daostack", + graphqlSubscribeToQueries: false, web3Provider: "ws://127.0.0.1:8545", web3ProviderRead: "ws://127.0.0.1:8545", ipfsProvider: "http://127.0.0.1:5001/api/v0", txSenderServiceUrl: "https://tx-sender-service.herokuapp.com/send-tx", + web3ConnectProviderOptions: {}, }, - staging: { + rinkeby: { graphqlHttpProvider: process.env.ARC_GRAPHQLHTTPPROVIDER || "https://api.thegraph.com/subgraphs/name/daostack/v37_3_rinkeby", graphqlWsProvider: process.env.ARC_GRAPHQLWSPROVIDER || "wss://api.thegraph.com/subgraphs/name/daostack/v37_3_rinkeby", + graphqlSubscribeToQueries: false, web3Provider: process.env.ARC_WEB3PROVIDER || "wss://rinkeby.infura.io/ws/v3/e0cdf3bfda9b468fa908aa6ab03d5ba2", web3ProviderRead: process.env.ARC_WEB3PROVIDERREAD || "wss://rinkeby.infura.io/ws/v3/e0cdf3bfda9b468fa908aa6ab03d5ba2", ipfsProvider: process.env.ARC_IPFSPROVIDER || "https://api.thegraph.com:443/ipfs-daostack/api/v0", txSenderServiceUrl: "https://tx-sender-service.herokuapp.com/send-tx", + web3ConnectProviderOptions: { + network: "rinkeby", + walletconnect: { + package: isMobileBrowser() ? null : WalletConnectProvider, + options: { + infuraId: "e0cdf3bfda9b468fa908aa6ab03d5ba2", + }, + }, + burnerconnect: { + package: BurnerConnectProvider, + options: { + defaultNetwork: "4", + defaultWallets: [ + { origin: "https://denver-demo.burnerfactory.com/", name: "Denver Demo Wallet" }, + ], + }, + }, + portis: { + package: Portis, + options: { + id: "aae9cff5-6e61-4b68-82dc-31a5a46c4a86", + }, + }, + fortmatic: { + package: Fortmatic, + options: { + key: "pk_test_659B5B486EF199E4", + }, + }, + squarelink: { + options: { + id: null as any, + }, + }, + }, }, - production: { + xdai: { + graphqlHttpProvider: process.env.ARC_GRAPHQLHTTPPROVIDER || "https://api.thegraph.com/subgraphs/name/daostack/v38_0_xdai", + graphqlWsProvider: process.env.ARC_GRAPHQLWSPROVIDER || "wss://api.thegraph.com/subgraphs/name/daostack/v38_0_xdai", + graphqlSubscribeToQueries: false, + web3Provider: process.env.ARC_WEB3PROVIDER || "https://poa.api.nodesmith.io/v1/dai/jsonrpc?apiKey=128059b9320a462699aef283a7ae2546", + web3ProviderRead: process.env.ARC_WEB3PROVIDERREAD || "wss://poa.api.nodesmith.io/v1/dai/jsonrpc/ws?apiKey=128059b9320a462699aef283a7ae2546", + ipfsProvider: process.env.ARC_IPFSPROVIDER || "https://api.thegraph.com:443/ipfs-daostack/api/v0", + txSenderServiceUrl: "", + web3ConnectProviderOptions: { + network: "xdao", + burnerconnect: { + package: BurnerConnectProvider, + options: { + defaultNetwork: "100", + defaultWallets: [ + { origin: "https://buffidao.com/", name: "BuffiDAO" }, + { origin: "https://denver-demo.burnerfactory.com/", name: "Denver Demo Wallet" }, + ], + }, + }, + }, + }, + main: { graphqlHttpProvider: process.env.ARC_GRAPHQLHTTPPROVIDER || "https://api.thegraph.com/subgraphs/name/daostack/v37_3", graphqlWsProvider: process.env.ARC_GRAPHQLWSPROVIDER || "wss://api.thegraph.com/subgraphs/name/daostack/v37_3", + graphqlSubscribeToQueries: false, web3Provider: process.env.ARC_WEB3PROVIDER || "wss://mainnet.infura.io/ws/v3/e0cdf3bfda9b468fa908aa6ab03d5ba2", web3ProviderRead: process.env.ARC_WEB3PROVIDERREAD || "wss://mainnet.infura.io/ws/v3/e0cdf3bfda9b468fa908aa6ab03d5ba2", ipfsProvider: process.env.ARC_IPFSPROVIDER || "https://api.thegraph.com:443/ipfs-daostack/api/v0", // txSenderServiceUrl: "https://tx-sender-service-mainnet.herokuapp.com/send-tx", txSenderServiceUrl: "", + web3ConnectProviderOptions: { + network: "mainnet", + walletconnect: { + package: isMobileBrowser() ? null : WalletConnectProvider, + options: { + infuraId: "e0cdf3bfda9b468fa908aa6ab03d5ba2", + }, + }, + burnerconnect: { + package: BurnerConnectProvider, + options: { + defaultNetwork: "1", + defaultWallets: [ + { origin: "https://buffidao.com/", name: "BuffiDAO" }, + ], + }, + }, + portis: { + package: Portis, + options: { + id: "aae9cff5-6e61-4b68-82dc-31a5a46c4a86", + }, + }, + fortmatic: { + package: Fortmatic, + options: { + key: "pk_live_38A2BD2B1D4E9912", + }, + }, + squarelink: { + options: { + id: null as any, + }, + }, + }, }, }; + + diff --git a/test/integration/utils.ts b/test/integration/utils.ts index d2d0a0fdc..7cb1bc242 100644 --- a/test/integration/utils.ts +++ b/test/integration/utils.ts @@ -1,5 +1,14 @@ import { Arc } from "@daostack/client"; -import { settings } from "../../src/settings"; + +const settings = { + graphqlHttpProvider: "http://127.0.0.1:8000/subgraphs/name/daostack", + graphqlWsProvider: "ws://127.0.0.1:8001/subgraphs/name/daostack", + graphqlSubscribeToQueries: false, + web3Provider: "ws://127.0.0.1:8545", + web3ProviderRead: "ws://127.0.0.1:8545", + ipfsProvider: "http://127.0.0.1:5001/api/v0", + txSenderServiceUrl: "https://tx-sender-service.herokuapp.com/send-tx", +}; const chai = require("chai"); @@ -48,7 +57,7 @@ export const userAddresses = [ ]; export function getArc() { - const arc = new Arc(settings.dev); + const arc = new Arc(settings); return arc; } diff --git a/typings/walletconnect.d.ts b/typings/walletconnect.d.ts new file mode 100644 index 000000000..f21520c05 --- /dev/null +++ b/typings/walletconnect.d.ts @@ -0,0 +1 @@ +declare module '@walletconnect/web3-provider \ No newline at end of file diff --git a/webpack.base.config.js b/webpack.base.config.js index 47ef17b50..bab9e0aaa 100644 --- a/webpack.base.config.js +++ b/webpack.base.config.js @@ -54,12 +54,15 @@ module.exports = { /node_modules\/apollo-link/, /node_modules\/apollo-link-http/, /node_modules\/apollo-link-ws/, - /node_modules\/xhr2-cookies/, + /node_modules\/ethereumjs-common/, + /node_modules\/ethereumjs-tx/, + /node_modules\/ethereumjs-util/, + /node_modules\/graphql-request/, + /node_modules\/https-did-resolver/, /node_modules\/rlp/, /node_modules\/subscriptions-transport-ws/, + /node_modules\/xhr2-cookies/, /node_modules\/zen-observable-ts/, - /node_modules\/graphql-request/, - /node_modules\/https-did-resolver/ ] },