import React, { Fragment, PureComponent } from 'react'; import web3 from 'web3'; import EmbarkJS from 'Embark/EmbarkJS'; import { connect } from 'react-redux'; import { actions as accountActions, getDefaultAccount } from '../../reducers/accounts'; import { hash } from 'eth-ens-namehash'; import { isNil } from 'lodash'; import Hidden from '@material-ui/core/Hidden'; import Typography from '@material-ui/core/Typography'; import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar'; import ENSRegistry from 'Embark/contracts/ENSRegistry'; import { Button, Field, TextInput, MobileSearch, MobileButton, Card, Info, Text } from '../../ui/components' import { IconCheck } from '../../ui/icons' import { keyFromXY } from '../../utils/ecdsa'; import EditOptions from './EditOptions'; import ReleaseDomainAlert from './ReleaseDomain'; import theme from '../../ui/theme' import { withFormik } from 'formik'; import PublicResolver from 'Embark/contracts/PublicResolver'; import { CopyToClipboard } from 'react-copy-to-clipboard'; import RegisterSubDomain from '../ens/registerSubDomain'; import StatusLogo from '../../ui/icons/components/StatusLogo' import EnsLogo from '../../ui/icons/logos/ens.png'; import { formatPrice } from '../ens/utils'; import CheckCircle from '../../ui/icons/components/baseline_check_circle_outline.png'; import WarningIcon from '../../ui/icons/svg/warning.svg'; const { getPrice, getReleaseTime, release } = UsernameRegistrar.methods; import NotInterested from '@material-ui/icons/NotInterested'; import Face from '@material-ui/icons/Face'; import Copy from './copy'; import IDNANormalizer from 'idna-normalize'; import { nullAddress, getResolver } from './utils/domain'; const normalizer = new IDNANormalizer(); const invalidSuffix = '0000000000000000000000000000000000000000' const validAddress = address => address != nullAddress; const validStatusAddress = address => !address.includes(invalidSuffix); const formatName = domainName => domainName.includes('.') ? normalizer.normalize(domainName) : normalizer.normalize(`${domainName}.stateofus.eth`); const getDomain = fullDomain => formatName(fullDomain).split('.').slice(1).join('.'); const hashedDomain = domainName => hash(getDomain(domainName)); const registryIsOwner = address => address == UsernameRegistrar._address; const { soliditySha3, fromWei } = web3.utils; const cardStyle = { width: '100%', padding: '30px', height: '425px' } const addressStyle = { fontSize: '18px', fontWeight: 400, cursor: 'copy', wordWrap: 'break-word', } const backButton = { fontSize: '40px', color: theme.accent, cursor: 'pointer' } const validTimestamp = timestamp => Number(timestamp) > 99999999; const generatePrettyDate = timestamp => new Date(timestamp * 1000).toDateString(); const DisplayBox = ({ displayType, pubKey }) => (
{displayType}
{pubKey}
); const MobileAddressDisplay = ({ domainName, address, statusAccount, releaseTime, defaultAccount, isOwner, edit, onSubmit, handleChange, values, handleSubmit }) => ( {isOwner ? : } {formatName(domainName)}
{isOwner ? edit ? 'Edit Contact Code' : 'You own this ENS name' : 'unavailable'}
Registered {validTimestamp(releaseTime)} {edit ? 'The contact code connects the domain with a unique Status account' : validAddress(address) ? 'to the addresses below' : 'Click \'Edit\' to add a valid address and contact code'} {edit && } {!edit && } {!edit && validStatusAddress(statusAccount) && }
) class RenderAddresses extends PureComponent { state = { copied: false, editMenu: false, editAction: false } render() { const { domainName, address, statusAccount, releaseTime, defaultAccount, ownerAddress, setStatus, registryOwnsDomain } = this.props const { copied, editMenu, editAction, submitted } = this.state const markCopied = (v) => { this.setState({ copied: v }) } const isCopied = address => address == copied; const renderCopied = address => isCopied(address) && Copied!; const onClose = value => { this.setState({ editAction: value, editMenu: false }) } const onClickEdit = () => { validAddress(address) ? this.setState({ editMenu: true }) : this.setState({ editAction: 'edit' }) } const isOwner = defaultAccount === ownerAddress; const closeReleaseAlert = value => { if (!isNil(value)) { this.setState({ submitted: true }) release( soliditySha3(domainName) ) .send() } else { this.setState({ editAction: null }) } } return (
{formatName(domainName)}{releaseTime && (Expires {generatePrettyDate(releaseTime)})} Resolves To: {address && Ethereum Address {renderCopied(address)}}
{address}
{validStatusAddress(statusAccount) && Status Address {renderCopied(statusAccount)}} {validStatusAddress(statusAccount) &&
{statusAccount}
}
{submitted ? : { this.setState({ submitted: true}) }}/>} {isOwner && !editAction && }
) } } const RegisterInfoCard = ({ formattedDomain, domainPrice, registryOwnsDomain }) => ( {formattedDomain.toLowerCase()} can be registered for {!!domainPrice && formatPrice(fromWei(domainPrice))} SNT {formattedDomain.toLowerCase()}
available
{!!domainPrice && formatPrice(fromWei(domainPrice))} SNT to register {registryOwnsDomain ? 'Add your contact code to use your name in Status chat.' : 'This domain is not owned by the registry'}
) const TransactionComplete = ({ type, setStatus }) => (
{Copy[type]['title']['sub']}
{Copy[type]['title']['body']}
{Copy[type]['subheading']} { setStatus(null) } } />
); class Register extends PureComponent { state = { domainPrice: null }; componentDidMount() { const { domainName } = this.props; getPrice() .call() .then((res) => { this.setState({ domainPrice: res })}); } onRegistered = (address, statusAccount) => { const { domainPrice } = this.state; const { subtractFromBalance } = this.props; subtractFromBalance(domainPrice); this.setState({ registered: { address, statusAccount } }); } render() { const { domainName, setStatus, style, registryOwnsDomain, ownerAddress, defaultAccount } = this.props; const { domainPrice, registered, submitted } = this.state; const formattedDomain = formatName(domainName); const formattedDomainArray = formattedDomain.split('.'); const isOwner = defaultAccount === ownerAddress; return (
{!registered && !submitted ? {registryOwnsDomain && { this.setState({ submitted: true }) }} registeredCallbackFn={this.onRegistered} />} : submitted && !registered ? : }
) } } const mapDispatchToProps = dispatch => ({ subtractFromBalance(amount) { dispatch(accountActions.subtractfromSntTokenBalance(amount)); }, }); const mapStateToProps = state => ({ defaultAccount: getDefaultAccount(state) }) const ConnectedRegister = connect(mapStateToProps, mapDispatchToProps)(Register); const DisplayAddress = connect(mapStateToProps)((props) => ( {validAddress(props.address) || props.defaultAccount === props.ownerAddress ? : {props.domainName} } )) const LookupForm = ({ handleSubmit, values, handleChange, justSearch }) => (
{!justSearch &&
Names are made with
letters and numbers only
}
) const InnerForm = ({ values, errors, touched, handleChange, handleBlur, handleSubmit, isSubmitting, status, setStatus, defaultAccount }) => (
Ens Logo {!status ? : validAddress(status.address) || defaultAccount === status.ownerAddress ? :
}
) const NameLookup = withFormik({ mapPropsToValues: props => ({ domainName: '' }), async handleSubmit(values, { status, setSubmitting, setStatus }) { const { domainName } = values; const { methods: { owner, resolver } } = ENSRegistry; const lookupHash = hash(formatName(domainName)); const resolverContract = await getResolver(lookupHash); const { addr, pubkey } = resolverContract.methods; const address = addr(lookupHash).call(); const keys = pubkey(lookupHash).call(); const ownerAddress = owner(lookupHash).call(); const suffixOwner = owner(hash(getDomain(domainName))).call(); const releaseTime = getReleaseTime(lookupHash).call(); Promise.all([address, keys, ownerAddress, releaseTime, suffixOwner]) .then(([ address, keys, ownerAddress, releaseTime, suffixOwner ]) => { const statusAccount = keyFromXY(keys[0], keys[1]); const registryOwnsDomain = registryIsOwner(suffixOwner) const resolvedDomainName = domainName; setStatus({ address, statusAccount, releaseTime, ownerAddress, registryOwnsDomain, resolvedDomainName }); }) } })(InnerForm) export default connect(mapStateToProps)(NameLookup);