diff --git a/package.json b/package.json index fc8f00cb1..9dfa53d5f 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "@emotion/styled": "^10.0.9", "@ensdomains/ens": "^0.3.3", "@ensdomains/ens-022": "npm:@ensdomains/ens@0.2.2", - "@ensdomains/ethregistrar": "^1.1.4", - "@ensdomains/resolver": "^0.1.4", + "@ensdomains/ethregistrar": "^1.2.2", + "@ensdomains/resolver": "^0.1.6", "apollo-cache-inmemory": "^1.2.9", "apollo-client": "^2.4.5", "apollo-link": "^1.2.2", @@ -34,6 +34,7 @@ "react-add-to-calendar": "^0.1.5", "react-apollo": "^2.1.2", "react-dom": "16.7.0-alpha.0", + "react-ga": "^2.5.7", "react-router": "^4.2.0", "react-router-dom": "^4.2.2", "react-scripts": "2.0.4", diff --git a/src/App.js b/src/App.js index f1e5bdaed..16b627282 100644 --- a/src/App.js +++ b/src/App.js @@ -20,6 +20,7 @@ import { NetworkError } from './components/Error/Errors' import { CONFIRM } from './modals' import DefaultLayout from './components/Layout/DefaultLayout' +import Analytics from './utils/analytics' const HomePageLayout = ({ children }) => {children} @@ -28,6 +29,7 @@ const Route = ({ layout: Layout = DefaultLayout, ...rest }) => { + Analytics.pageview() return ( ( <> {({ data }) => { + Analytics.setup() if (data.error && data.error.message) { return } else { diff --git a/src/api/registrar.js b/src/api/registrar.js index 8dbc9e6e2..c78d32bec 100644 --- a/src/api/registrar.js +++ b/src/api/registrar.js @@ -241,9 +241,26 @@ export const transferOwner = async ({ to, name }) => { const labelHash = web3.utils.sha3(nameArray[0]) const account = await getAccount() const { permanentRegistrarRead: Registrar } = await getPermanentRegistrar() - return Registrar.safeTransferFrom(account, to, labelHash).send({ - from: account - }) + return () => + Registrar.safeTransferFrom(account, to, labelHash).send({ + from: account + }) + } catch (e) { + console.log('error getting permanentRegistrar contract', e) + } +} + +export const reclaim = async ({ name, address }) => { + try { + const web3 = await getWeb3() + const nameArray = name.split('.') + const labelHash = web3.utils.sha3(nameArray[0]) + const account = await getAccount() + const { permanentRegistrarRead: Registrar } = await getPermanentRegistrar() + return () => + Registrar.reclaim(labelHash, address).send({ + from: account + }) } catch (e) { console.log('error getting permanentRegistrar contract', e) } @@ -305,6 +322,19 @@ export const register = async (label, duration, secret) => { .send({ from: account, gas: 1000000, value: price }) } +export const renew = async (label, duration) => { + const { + permanentRegistrarController + } = await getPermanentRegistrarController() + const account = await getAccount() + const price = await getRentPrice(label, duration) + + return () => + permanentRegistrarController + .renew(label, duration) + .send({ from: account, gas: 1000000, value: price }) +} + export const createSealedBid = async (name, bidAmount, secret) => { const Registrar = await getLegacyAuctionRegistrar() const web3 = await getWeb3() @@ -353,7 +383,7 @@ export const transferRegistrars = async label => { const web3 = await getWeb3() const hash = web3.utils.sha3(label) const tx = ethRegistrar.transferRegistrars(hash) - const gas = await tx.estimateGas({from:account}) + const gas = await tx.estimateGas({ from: account }) return () => tx.send({ from: account, @@ -367,7 +397,7 @@ export const releaseDeed = async label => { const web3 = await getWeb3() const hash = web3.utils.sha3(label) const tx = ethRegistrar.releaseDeed(hash) - const gas = await tx.estimateGas({from:account}) + const gas = await tx.estimateGas({ from: account }) return () => tx.send({ from: account, diff --git a/src/api/registrar/resolvers.js b/src/api/registrar/resolvers.js index 4bf369eb9..e0379dba3 100644 --- a/src/api/registrar/resolvers.js +++ b/src/api/registrar/resolvers.js @@ -6,9 +6,11 @@ import { commit, getMinimumCommitmentAge, register, + renew, transferRegistrars, releaseDeed, - transferOwner + transferOwner, + reclaim } from '../registrar' import { getOwner } from '../registry' import modeNames from '../modes' @@ -46,6 +48,14 @@ const resolvers = { return sendHelper(tx) }, + async reclaim(_, { name, address }) { + const tx = await reclaim({ name, address }) + return sendHelper(tx) + }, + async renew(_, { label, duration }) { + const tx = await renew(label, duration) + return sendHelper(tx) + }, async getDomainAvailability(_, { name }, { cache }) { try { const { @@ -89,7 +99,7 @@ const resolvers = { } }, async setRegistrant(_, { name, address }) { - const tx = transferOwner({ name, to: address }) + const tx = await transferOwner({ name, to: address }) return sendHelper(tx) }, async transferRegistrars(_, { label }) { diff --git a/src/components/Modal/Modal.js b/src/components/Modal/Modal.js index 4522e3ad6..8393a2c03 100644 --- a/src/components/Modal/Modal.js +++ b/src/components/Modal/Modal.js @@ -34,21 +34,25 @@ const ModalContainer = styled('div')` top: 0; width: 100%; height: 100%; - padding: 20px; + padding: 0; display: flex; justify-content: center; align-items: center; z-index: 99999999; background: rgba(0, 0, 0, 0.5); + + ${mq.small` + padding: 20px; + `}; ` const ModalContent = styled('div')` background: white; - padding: 40px; + padding: 20px; overflow-y: scroll; - height: auto%; - min-width: 850px; + height: auto; ${mq.medium` + padding: 40px; width: 70%; `}; diff --git a/src/components/SingleName/AddRecord.js b/src/components/SingleName/AddRecord.js index 327ee1123..59a003c60 100644 --- a/src/components/SingleName/AddRecord.js +++ b/src/components/SingleName/AddRecord.js @@ -32,7 +32,8 @@ const Select = styled(DefaultSelect)` const RecordsTitle = styled('h3')` /* Pointers: */ - font-family: Overpass-Bold; + font-family: Overpass; + font-weight: 700; font-size: 12px; color: #adbbcd; letter-spacing: 0.5px; diff --git a/src/components/SingleName/Confirm.js b/src/components/SingleName/Confirm.js index ac62ce8e4..b5bd86763 100644 --- a/src/components/SingleName/Confirm.js +++ b/src/components/SingleName/Confirm.js @@ -2,24 +2,37 @@ import React from 'react' import styled from '@emotion/styled' import Button from '../Forms/Button' import warning from '../../assets/warning.svg' -import write from '../../assets/Write.svg' +import write from '../../assets/Write.svg' + +import mq from 'mediaQuery' const ConfirmContainer = styled('div')` &:before { + display: none; background: url(${write}); content: ''; height: 43px; width: 42px; float: right; + ${mq.large` + display: block; + flex-direction: row; + `} } ` const Content = styled('div')` display: flex; justify-content: space-between; + flex-direction: column; ` const Title = styled('h3')` - margin:0 0 0 1.5em; + margin: 0 0 0 1.5em; + font-size: 16px; + font-weight: 300; + ${mq.small` + font-size: 22px; + `} &:before { background: url(${warning}); content: ''; @@ -32,15 +45,18 @@ const Title = styled('h3')` ` const SubTitle = styled('p')` - margin:0; - margin-bottom:1em; + margin: 0; + margin-bottom: 1em; + font-size: 12px; + ${mq.small` + font-size: 14px; + `} ` const Values = styled('ul')` list-style-type: none; - width:30em; padding: 4px 0; - margin:0; + margin: 0; display: flex; flex-direction: column; justify-content: space-between; @@ -53,11 +69,21 @@ const Value = styled('li')` display: flex; justify-content: space-between; color: ${({ old }) => (old ? 'grey' : 'black')}; + margin-bottom: 10px; + + ${mq.large` + justify-content: flex-start; + `} + + span:first-child { + width: 75px; + } ` const Buttons = styled('div')` - height:100%; - margin-top:1em; + height: 100%; + margin-top: 1em; + display: flex; ` const Action = styled(Button)`` @@ -79,24 +105,36 @@ const Confirm = ({ Are you sure you want to do this? This action will modify the state of the blockchain. - {explanation ? (

{explanation}

): ''} + {explanation ?

{explanation}

: ''} {value || newValue ? ( - - PREVIOUS{value} - FUTURE{newValue} - - ): ''} + + + PREVIOUS + {value} + + + FUTURE + {newValue} + + + ) : ( + '' + )} - Cancel + Cancel {disabled ? ( - Confirm + Confirm ) : ( - { - mutation() - cancel() - }}>Confirm + { + mutation() + cancel() + }} + > + Confirm + )}
diff --git a/src/components/SingleName/DetailsItemEditable.js b/src/components/SingleName/DetailsItemEditable.js index 6d801c049..7796fbcb1 100644 --- a/src/components/SingleName/DetailsItemEditable.js +++ b/src/components/SingleName/DetailsItemEditable.js @@ -1,14 +1,16 @@ -import React, { Component } from 'react' +import React, { Component, useState } from 'react' import styled from '@emotion/styled' import { Mutation, Query } from 'react-apollo' import { addressUtils } from '@0xproject/utils' import PropTypes from 'prop-types' import { Transition } from 'react-spring' -import Tooltip from '../Tooltip/Tooltip' + import { GET_PUBLIC_RESOLVER } from '../../graphql/queries' import mq from 'mediaQuery' -import { useEditable } from '../hooks' +import { useEditable, useEthPrice } from '../hooks' +import { yearInSeconds, formatDate } from 'utils/dates' +import Tooltip from '../Tooltip/Tooltip' import { SingleNameBlockies } from './SingleNameBlockies' import DefaultEtherScanLink from '../ExternalLinks/EtherScanLink' import { DetailsItem, DetailsKey, DetailsValue } from './DetailsItem' @@ -18,6 +20,7 @@ import Button from '../Forms/Button' import Pencil from '../Forms/Pencil' import DefaultInfo from '../Icons/Info' import DefaultPendingTx from '../PendingTx' +import DefaultPricer from './Pricer' const EtherScanLink = styled(DefaultEtherScanLink)` display: flex; @@ -95,6 +98,8 @@ const DefaultResolverButton = styled('a')` } ` +const Pricer = styled(DefaultPricer)`` + const Buttons = styled('div')` display: flex; justify-content: flex-end; @@ -105,13 +110,82 @@ function getDefaultMessage(keyName) { switch (keyName) { case 'Resolver': return ['No Resolver set', 'message'] - case 'Owner': + case 'Controller': return ['Not owned yet', 'message'] default: return ['No 0x message set', 'message'] } } +function getInputType( + keyName, + { + newValue, + updateValue, + isValid, + isInvalid, + name, + ethUsdPriceLoading, + ethUsdPrice, + years, + setYears, + duration, + expirationDate + } +) { + switch (keyName) { + case 'Expiration Date': + return ( + { + setYears(years) + updateValue(formatDate(expirationDate)) + }} + ethUsdPriceLoading={ethUsdPriceLoading} + ethUsdPrice={ethUsdPrice} + expirationDate={expirationDate} + /> + ) + default: + return ( + updateValue(e.target.value)} + valid={isValid} + invalid={isInvalid} + placeholder="Type in a new Ethereum address" + large + /> + ) + } +} + +function getValidation(keyName, newValue) { + switch (keyName) { + case 'Expiration Date': + return true + default: + return addressUtils.isAddress(newValue) + } +} + +function getVariables(keyName, { domain, variableName, newValue, duration }) { + if (keyName === 'Expiration Date') { + return { + label: domain.name.split('.')[0], + duration + } + } else { + return { + name: domain.name, + [variableName ? variableName : 'address']: newValue + } + } +} + const Editable = ({ keyName, value, @@ -136,7 +210,19 @@ const Editable = ({ setConfirmed } = actions - const isValid = addressUtils.isAddress(newValue) + //only used with Expiration date + let duration + let expirationDate + const [years, setYears] = useState(1) + const { price: ethUsdPrice, loading: ethUsdPriceLoading } = useEthPrice( + keyName === 'Expiration Date' + ) + if (keyName === 'Expiration Date') { + duration = parseFloat(years) * yearInSeconds + expirationDate = new Date(new Date(value).getTime() + duration * 1000) + } + + const isValid = getValidation(keyName, newValue) const isInvalid = !isValid && newValue.length > 0 return ( @@ -182,6 +268,8 @@ const Editable = ({ ) : null}
{value}
+ ) : type === 'date' ? ( + formatDate(value) ) : ( value )} @@ -223,14 +311,19 @@ const Editable = ({ (props => (
- updateValue(e.target.value)} - valid={isValid} - invalid={isInvalid} - placeholder="Type in a new Ethereum address" - large - /> + {getInputType(keyName, { + newValue, + updateValue, + isValid, + isInvalid, + years, + name: domain.name, + setYears, + ethUsdPrice, + ethUsdPriceLoading, + duration, + expirationDate + })} {keyName === 'Resolver' && ( @@ -254,17 +347,24 @@ const Editable = ({ { - const variables = { - name: domain.name, - [variableName ? variableName : 'address']: newValue - } - - mutation({ - variables + const variables = getVariables(keyName, { + domain, + variableName, + newValue, + duration }) + mutation({ variables }) }} - value={value} - newValue={newValue} + value={ + keyName === 'Expiration Date' + ? formatDate(value) + : value + } + newValue={ + keyName === 'Expiration Date' + ? formatDate(expirationDate) + : newValue + } mutationButton={mutationButton} confirm={confirm} isValid={isValid} diff --git a/src/components/SingleName/NameDetails.js b/src/components/SingleName/NameDetails.js index a07821e99..81fa22fcd 100644 --- a/src/components/SingleName/NameDetails.js +++ b/src/components/SingleName/NameDetails.js @@ -17,7 +17,9 @@ import { SET_ADDRESS, SET_CONTENT, SET_CONTENTHASH, - SET_REGISTRANT + SET_REGISTRANT, + RECLAIM, + RENEW } from '../../graphql/mutations' import NameClaimTestDomain from './NameClaimTestDomain' @@ -149,7 +151,7 @@ class NameDetails extends Component { <> @@ -177,7 +179,7 @@ class NameDetails extends Component { ) : ( - Expiration Date - - {formatDate(domain.expiryTime)} - - + domain.isNewRegistrar && isOwner ? ( + + ) : ( + + Expiration Date + + {formatDate(domain.expiryTime)} + + + ) ) : ( '' )} diff --git a/src/components/SingleName/NameRegister/NameRegister.js b/src/components/SingleName/NameRegister/NameRegister.js index b8c095ba4..1bdb1dc97 100644 --- a/src/components/SingleName/NameRegister/NameRegister.js +++ b/src/components/SingleName/NameRegister/NameRegister.js @@ -2,46 +2,22 @@ import React, { useState, useReducer } from 'react' import styled from '@emotion/styled' import { Query } from 'react-apollo' -import mq from 'mediaQuery' -import { GET_MINIMUM_COMMITMENT_AGE, GET_RENT_PRICE } from 'graphql/queries' +import { GET_MINIMUM_COMMITMENT_AGE } from 'graphql/queries' import { useInterval, useEthPrice } from 'components/hooks' import { registerMachine, registerReducer } from './registerReducer' import { sendNotification } from './notification' +import { yearInSeconds } from 'utils/dates' import Loader from 'components/Loader' import Explainer from './Explainer' import CTA from './CTA' -import Years from './Years' -import Price from './Price' import Progress from './Progress' -import { ReactComponent as ChainDefault } from '../../Icons/chain.svg' - -const PricingContainer = styled('div')` - display: grid; - grid-template-columns: 1fr; - margin-bottom: 20px; - ${mq.medium` - grid-template-columns: - minmax(min-content, 200px) minmax(min-content, min-content) - minmax(200px, 1fr); - `} -` +import Pricer from '../Pricer' const NameRegisterContainer = styled('div')` padding: 20px 40px; ` -const Chain = styled(ChainDefault)` - display: none; - - ${mq.medium` - display: block; - margin-top: 20px; - margin-left: 20px; - margin-right: 20px; - `} -` - const NameRegister = ({ domain, waitTime, refetch }) => { const [step, dispatch] = useReducer( registerReducer, @@ -68,7 +44,6 @@ const NameRegister = ({ domain, waitTime, refetch }) => { ) const parsedYears = parseFloat(years) - const yearInSeconds = 31556952 const duration = yearInSeconds * parsedYears const oneMonthInSeconds = 2419200 const twentyEightDaysInYears = oneMonthInSeconds / yearInSeconds @@ -78,31 +53,14 @@ const NameRegister = ({ domain, waitTime, refetch }) => { return ( {step === 'PRICE_DECISION' && ( - - {({ data, loading }) => { - return ( - - - - - - ) - }} - + )} { @@ -36,7 +43,7 @@ const Price = ({ price, ethUsdPrice, ethUsdPriceLoading }) => { return ( - {ethVal.toFixed(5)} ETH + {ethVal.toFixed(3)} ETH {!ethUsdPriceLoading && ( ${ethVal.mul(ethUsdPrice).toFixed(2)} USD )} diff --git a/src/components/SingleName/NameRegister/Years.js b/src/components/SingleName/NameRegister/Years.js index b4c836393..47f2ffd28 100644 --- a/src/components/SingleName/NameRegister/Years.js +++ b/src/components/SingleName/NameRegister/Years.js @@ -50,6 +50,7 @@ const Amount = styled('div')` align-self: center; input { + background: transparent; font-family: Overpass; font-size: 28px; font-weight: 100; diff --git a/src/components/SingleName/Pricer.js b/src/components/SingleName/Pricer.js new file mode 100644 index 000000000..92f944398 --- /dev/null +++ b/src/components/SingleName/Pricer.js @@ -0,0 +1,67 @@ +import React from 'react' +import styled from '@emotion/styled' +import { Query } from 'react-apollo' + +import { GET_RENT_PRICE } from 'graphql/queries' + +import Years from './NameRegister/Years' +import Price from './NameRegister/Price' + +import mq from 'mediaQuery' + +import { ReactComponent as ChainDefault } from '../Icons/chain.svg' + +const PricingContainer = styled('div')` + display: grid; + grid-template-columns: 1fr; + margin-bottom: 20px; + ${mq.medium` + grid-template-columns: + minmax(min-content, 200px) minmax(min-content, min-content) + minmax(200px, 1fr); + `} +` +const Chain = styled(ChainDefault)` + display: none; + + ${mq.medium` + display: block; + margin-top: 20px; + margin-left: 20px; + margin-right: 20px; + `} +` + +export default function Pricer({ + years, + setYears, + duration, + ethUsdPriceLoading, + ethUsdPrice, + name, + className +}) { + return ( + + {({ data, loading }) => { + return ( + + + + + + ) + }} + + ) +} diff --git a/src/components/hooks.js b/src/components/hooks.js index 563b938f4..60c929a3d 100644 --- a/src/components/hooks.js +++ b/src/components/hooks.js @@ -97,16 +97,18 @@ export function useInterval(callback, delay) { }, [delay]) } -export function useEthPrice() { +export function useEthPrice(enabled = true) { const [loading, setLoading] = useState(true) const [price, setPrice] = useState(undefined) useEffect(() => { - getEtherPrice().then(res => { - setPrice(res.result.ethusd) - setLoading(false) - }) - }, []) + if (enabled) { + getEtherPrice().then(res => { + setPrice(res.result.ethusd) + setLoading(false) + }) + } + }, [enabled]) return { loading, diff --git a/src/graphql/mutations.js b/src/graphql/mutations.js index 327233b4d..2cc7e04ef 100644 --- a/src/graphql/mutations.js +++ b/src/graphql/mutations.js @@ -109,15 +109,27 @@ export const SET_REGISTRANT = gql` } ` +export const RECLAIM = gql` + mutation reclaim($name: String, $address: String) { + reclaim(name: $name, address: $address) @client + } +` + export const COMMIT = gql` - mutation commit($label: String, $owner: String, $secret: String) { - commit(label: $label, owner: $owner, secret: $secret) @client + mutation commit($label: String) { + commit(label: $label) @client } ` export const REGISTER = gql` mutation register($label: String, $duration: Int) { - register(label: $label, duration: $duration, secret: $secret) @client + register(label: $label, duration: $duration) @client + } +` + +export const RENEW = gql` + mutation renew($label: String, $duration: Int) { + renew(label: $label, duration: $duration) @client } ` diff --git a/src/routes/SearchResults.js b/src/routes/SearchResults.js index fe6ee9fc9..2b257d468 100644 --- a/src/routes/SearchResults.js +++ b/src/routes/SearchResults.js @@ -1,8 +1,6 @@ import React, { Fragment } from 'react' import DomainInfo from '../components/SearchName/DomainInfo' -import SubDomainResults from '../components/SubDomainResults/SubDomainResults' import { SubDomainStateFields } from '../graphql/fragments' -import { GET_SINGLE_NAME } from '../graphql/queries' import { Mutation } from 'react-apollo' import gql from 'graphql-tag' import { parseSearchTerm } from '../utils/utils' @@ -24,7 +22,7 @@ class Results extends React.Component { errorType: '' } checkValidity = () => { - const { searchTerm, getDomainState, getSubDomainAvailability } = this.props + const { searchTerm, getSubDomainAvailability } = this.props this.setState({ errors: [] diff --git a/src/routes/SingleName.js b/src/routes/SingleName.js index 9a9e6dd60..f74d5f219 100644 --- a/src/routes/SingleName.js +++ b/src/routes/SingleName.js @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react' -import { validateName } from '../utils/utils' +import { validateName, parseSearchTerm } from '../utils/utils' import { GET_SINGLE_NAME } from '../graphql/queries' import { Query } from 'react-apollo' import Loader from '../components/Loader' @@ -14,19 +14,30 @@ function SingleName({ location: { pathname } }) { const [valid, setValid] = useState(undefined) + const [type, setType] = useState(undefined) const [name, setNormalisedName] = useState('') + let normalisedName, errorMessage, _type useEffect(() => { try { // This is under the assumption that validateName never returns false - const normalisedName = validateName(searchTerm) - setValid(true) + normalisedName = validateName(searchTerm) setNormalisedName(normalisedName) - document.title = searchTerm } catch { - setValid(false) document.title = 'Error finding name' + } finally { + if (normalisedName) { + _type = parseSearchTerm(normalisedName) + } else { + _type = parseSearchTerm(searchTerm) + } + setType(_type) + if (_type === 'supported') { + setValid(true) + } else { + setValid(false) + } } }, [searchTerm]) @@ -49,7 +60,19 @@ function SingleName({ ) } else if (valid === false) { - return + if (type === 'invalid') { + errorMessage = 'domainMalformed' + } else if (type === 'short') { + errorMessage = 'tooShort' + } else { + errorMessage = type + } + return ( + + ) } else { return } diff --git a/src/testing-utils/deployENS.js b/src/testing-utils/deployENS.js index b33079a57..0a19d4aa5 100644 --- a/src/testing-utils/deployENS.js +++ b/src/testing-utils/deployENS.js @@ -38,12 +38,11 @@ const registerName = async function(web3, account, controllerContract, name) { .call() await controllerContract.commit(commitment).send({ from: account }) var minCommitmentAge = await controllerContract.minCommitmentAge().call() - await advanceTime(web3, parseInt(minCommitmentAge)) + const time = await advanceTime(web3, parseInt(minCommitmentAge)) await mine(web3) - await controllerContract .register(name, account, 28 * DAYS, secret) - .send({ from: account, value: VALUE, gas: 5000000 }) + .send({ from: account, value: VALUE, gas: 6000000 }) // The name should be no longer available newnameAvailable = await controllerContract.available(name).call() @@ -292,6 +291,8 @@ async function deployENS({ web3, accounts }) { .register(sha3('example'), accounts[0]) .send({ from: accounts[0] }) + console.log('registered example.test') + /* Setup the root reverse node */ await ensContract .setSubnodeOwner( @@ -303,16 +304,22 @@ async function deployENS({ web3, accounts }) { from: accounts[0] }) + console.log('setup root reverse') + await ensContract .setSubnodeOwner(namehash('reverse'), sha3('addr'), accounts[0]) .send({ from: accounts[0] }) + console.log('setup root reverse with addr label') + await ensContract .setResolver(namehash('addr.reverse'), resolver._address) .send({ from: accounts[0] }) + console.log('setup root reverse with public resolver') + /* Setup the reverse subdomain: addr.reverse */ await ensContract .setSubnodeOwner( @@ -324,6 +331,8 @@ async function deployENS({ web3, accounts }) { from: accounts[0] }) + console.log('setup root reverse with the reverse registrar') + /* Set the old hash registrar contract as the owner of .eth */ await ensContract .setSubnodeOwner( @@ -335,14 +344,18 @@ async function deployENS({ web3, accounts }) { from: accounts[0] }) + console.log('Successfully setup old hash registrar') + const now = (await web3.eth.getBlock('latest')).timestamp const priceOracle = await deploy(priceOracleJSON, 1) const baseRegistrar = await deploy( baseRegistrarJSON, ens._address, + legacyAuctionRegistrar._address, namehash('eth'), now + 365 * DAYS ) + console.log('Successfully setup base registrar') const controller = await deploy( controllerJSON, baseRegistrar._address, @@ -350,6 +363,8 @@ async function deployENS({ web3, accounts }) { 2, // 10 mins in seconds 86400 // 24 hours in seconds ) + + console.log('Successfully setup permanent registrar controller') const baseRegistrarContract = baseRegistrar.methods const controllerContract = controller.methods @@ -406,15 +421,19 @@ async function deployENS({ web3, accounts }) { .send({ from: accounts[0] }) console.log('Register name') - await registerName(web3, accounts[0], controllerContract, 'newname') - await registerName(web3, accounts[0], controllerContract, 'resolver') - await registerName(web3, accounts[0], controllerContract, 'oldresolver') - await registerName(web3, accounts[0], controllerContract, 'awesome') - await registerName(web3, accounts[0], controllerContract, 'superawesome') - await registerName(web3, accounts[0], controllerContract, 'notsoawesome') - await registerName(web3, accounts[0], controllerContract, 'abittooawesome') - await registerName(web3, accounts[0], controllerContract, 'subdomaindummy') - await registerName(web3, accounts[0], controllerContract, 'subdomain') + try { + await registerName(web3, accounts[0], controllerContract, 'newname') + await registerName(web3, accounts[0], controllerContract, 'resolver') + await registerName(web3, accounts[0], controllerContract, 'oldresolver') + await registerName(web3, accounts[0], controllerContract, 'awesome') + await registerName(web3, accounts[0], controllerContract, 'superawesome') + await registerName(web3, accounts[0], controllerContract, 'notsoawesome') + await registerName(web3, accounts[0], controllerContract, 'abittooawesome') + await registerName(web3, accounts[0], controllerContract, 'subdomaindummy') + await registerName(web3, accounts[0], controllerContract, 'subdomain') + } catch (e) { + console.log('Failed to register a name', e) + } await ensContract .setResolver(namehash('notsoawesome.eth'), resolver._address) diff --git a/src/utils/analytics.js b/src/utils/analytics.js new file mode 100644 index 000000000..f734c899b --- /dev/null +++ b/src/utils/analytics.js @@ -0,0 +1,23 @@ +import ReactGA from 'react-ga'; +const TrackingID = 'UA-138903307-1' + +function isProduction(){ + return window.location.host === 'manager.ens.domains' +} + +export const setup = () => { + if(isProduction()){ + ReactGA.initialize(TrackingID); + } +} + +export const pageview = () => { + const page = window.location.pathname + window.location.search + if(isProduction()){ + ReactGA.pageview(page); + } +} + +export default { + setup, pageview +} diff --git a/src/utils/dates.js b/src/utils/dates.js index 2dca85245..0041580b6 100644 --- a/src/utils/dates.js +++ b/src/utils/dates.js @@ -2,7 +2,7 @@ import moment from 'moment' export function formatDate(unixTimeStamp, noTime) { let date = moment(unixTimeStamp).format('YYYY.MM.DD') - if(!noTime){ + if (!noTime) { date = date + ' at ' + moment(unixTimeStamp).format('hh:mm:ss') } return date @@ -25,3 +25,5 @@ export function humanizeDate(timeLeft) { .toFixed(0)}m` } } + +export const yearInSeconds = 31556952 diff --git a/yarn.lock b/yarn.lock index f8ac196c7..17bb52d96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -980,23 +980,23 @@ testrpc "0.0.1" web3-utils "^1.0.0-beta.31" -"@ensdomains/ethregistrar@^1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@ensdomains/ethregistrar/-/ethregistrar-1.1.4.tgz#d6bb288502302203729e7c97e5e357eb439d4e19" - integrity sha512-kAprCgxb8Q+JIKtVGAug9Ma8zpE0P7KtY9YBcq2U5vJ1JR0Bgv5TKhgkVsCjfq/Hw0r5JgwFxgFqgLlTWr8uJA== +"@ensdomains/ethregistrar@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@ensdomains/ethregistrar/-/ethregistrar-1.2.2.tgz#4b770cad47a805cf0629d255cfcd5998d7b704e2" + integrity sha512-F0XH1jGqy0M1k5YNlkOz+ZUSIMxiijsRvS59WJrsQiMa2fUcSX+snB/lZBpkoZTd0xu5bwDYlKlVHmzGxmYArg== dependencies: "@ensdomains/ens" "^0.3.4" bluebird "^3.5.3" eth-ens-namehash "^2.0.8" - openzeppelin-solidity "^2.1.3" + openzeppelin-solidity "2.1.3" solium "^1.2.3" truffle "^5.0.5" web3-utils "^1.0.0-beta.48" -"@ensdomains/resolver@^0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.1.4.tgz#5933b94d1b24521d08153a09c60a939f7cc9543b" - integrity sha512-hF4lm8tcao8WWowJEBihii53C3/wDDT2vz+1WKhAJLMKAKyBjz2DQTGuIJE0aPGzFKl8GFR6Be/OxqF6U9reGA== +"@ensdomains/resolver@^0.1.6": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.1.6.tgz#15e070a58d5f2be6b46787661121dddae0cfe132" + integrity sha512-6+xbVrajjDJJiTqtqjl18tO98YM3ceiNCOwjDzI/LZU9PadJZlKreGTgaZOQJi4bj5hyXZf3xwRU2tJOHSaPpQ== "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" @@ -8767,7 +8767,7 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -openzeppelin-solidity@^2.1.3: +openzeppelin-solidity@2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/openzeppelin-solidity/-/openzeppelin-solidity-2.1.3.tgz#34aea40c2d05665f0dd6bc021e48516cf25b1ef6" integrity sha512-Zz595xw6w4ZrNU3JJJTlPd8bHFbHv5OkJlkqsM2i+NBs6CzImoHI3dZ+nmhkfR+p52cGXKmayAGbnfdCum1SMg== @@ -10112,6 +10112,11 @@ react-error-overlay@^5.0.4: version "5.0.4" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.0.4.tgz#39cf184d770f98b65a2ee59a779d03b8d092b69e" +react-ga@^2.5.7: + version "2.5.7" + resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-2.5.7.tgz#1c80a289004bf84f84c26d46f3a6a6513081bf2e" + integrity sha512-UmATFaZpEQDO96KFjB5FRLcT6hFcwaxOmAJZnjrSiFN/msTqylq9G+z5Z8TYzN/dbamDTiWf92m6MnXXJkAivQ== + react-input-autosize@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.2.1.tgz#ec428fa15b1592994fb5f9aa15bb1eb6baf420f8"