ENS Resolution for AddressField Component (#807)
* Refactor BaseNode to be an interface INode * Initial contract commit * Remove redundant fallback ABI function * First working iteration of Contract generator to be used in ENS branch * Hide abi to clean up logging output * Strip 0x prefix from output decode * Handle unnamed output params * Implement ability to supply output mappings to ABI functions * Fix null case in outputMapping * Add flow typing * Add .call method to functions * Partial commit for type refactor * Temp contract type fix -- waiting for NPM modularization * Remove empty files * Cleanup contract * Add call request to node interface * Fix output mapping types * Revert destructuring overboard * Add sendCallRequest to rpcNode class and add typing * Use enum for selecting ABI methods * Add transaction capability to contracts * Cleanup privaite/public members * Remove broadcasting step from a contract transaction * Cleanup uneeded types * Refactor ens-base to typescript and add typings for ENS smart contracts * Migrate ens-name-search to TS * Add IResolveDomainRequest * Fix rest of TSC errors * Add definition file for bn.js * Remove types-bn * Fix some typings * make isBN a static property * progress commit -- swap out bignumber.js for bn.js * Swap out bignumber for bn in vendor * Change modn to number return * Start to strip out units lib for a string manipulation based lib * Convert codebase to only base units * Get rid of useless component * Handle only wei in values * Use unit conversion in sidebar * Automatically strip hex prefix, and handle decimal edge case * Handle base 16 wei in transactions * Make a render callback component for dealing with unit conversion * Switch contracts to use bn.js, and get transaction values from signedTx instead of state * Get send transaction working with bn.js * Remove redundant hex stripping, return base value of tokens * Cleanup unit file * Re-implement toFixed for strings * Use formatNumber in codebase * Cleanup code * Undo package test changes * Update snapshot and remove console logs * Use TokenValue / Wei more consistently where applicable * Add typing to deterministicWallets, fix confirmation modal, make UnitDisplay more flexible * Split different ENS modes into their own components * Fix Abi typedef * Remove redundant moment type package * Add Aux helper component * Split out resolve components * Make 'to' parameter optional * Change import type * Change typing to be base domain request * Split handling of resolving into object handler * Fix countdown component * Adjust element spacing * Implement reveal search functionality * Add unit display for highest bidder * Fill out forbidden/NYA modes * ENS wallet component skeleton * Clean up prop handling in UnitDisplay * Change instanceof to typeof check, change boolean of displayBalance * Add ENS wallet component * Cleanup spacing * Convert ConfModal for bidding in ENS * Make ui component for placing bids * Fix destructure in placeBid * Pass through entire wallet * Remove text center * Display inline notification ENS isValid & add some ENS tests * Add export of Aux * Reformat with prettier * progress... * Add ENSUnlockLayout * Add RevealBid component * organize NameResolve components * Merge ENS with transaction-refactor changes * Fix address resolution * Update styles * convert ens name to lowercase before checking * Add overflow-y:scroll to table * update ens snapshots & tests * cast 'undefined' state argument as any for testing * clean up components * Connect unitconverter to redux state * remove unnecessary type assertion * fix spinner size * remove old bidmodal * validate bidmask before opening modal * progress... * Update styles * Add saga / actions for placing a bid * Update types & clean up dead code * Delete old test * Dispatch PlaceBidRequested acitons * Progress commit -- get ENS bidding ready for tx generation via sagas * Seperate ENS action creators and types * Add reducer & actions for ENS fields * Add preliminary sagas for bid mask and bid value * Initial commit * Add loading indicator * Remove some bidding components * Revert bidding files * Remove more bidding code * Remove rest of bidding code * Fix ENS error message * Revert value saga changes * Remove error param from setting 'To' field * Fix existing ENS test * Cleanup address resolution, remove dead code * Remove error messages from unimplemented ENS * Fix last character being not set bug * Remove error state from Meta * Rename isGenesisAddress to isCreationAddress
This commit is contained in:
parent
eb4fd1cce8
commit
67b2e6491c
|
@ -1,22 +0,0 @@
|
||||||
import * as interfaces from './actionTypes';
|
|
||||||
import * as constants from './constants';
|
|
||||||
|
|
||||||
export function resolveEnsName(name: string): interfaces.ResolveEnsNameAction {
|
|
||||||
return {
|
|
||||||
type: constants.ENS_RESOLVE,
|
|
||||||
payload: name
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function cacheEnsAddress(
|
|
||||||
ensName: string,
|
|
||||||
address: string
|
|
||||||
): interfaces.CacheEnsAddressAction {
|
|
||||||
return {
|
|
||||||
type: constants.ENS_CACHE,
|
|
||||||
payload: {
|
|
||||||
ensName,
|
|
||||||
address
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './resolveDomain';
|
|
@ -0,0 +1,35 @@
|
||||||
|
import * as ActionTypes from '../actionTypes';
|
||||||
|
import { TypeKeys } from '../constants';
|
||||||
|
import { DomainRequest } from 'libs/ens';
|
||||||
|
import { ResolveDomainCached } from 'actions/ens';
|
||||||
|
|
||||||
|
export type TResolveDomainRequested = typeof resolveDomainRequested;
|
||||||
|
export const resolveDomainRequested = (domain: string): ActionTypes.ResolveDomainRequested => ({
|
||||||
|
type: TypeKeys.ENS_RESOLVE_DOMAIN_REQUESTED,
|
||||||
|
payload: { domain }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const resolveDomainCached = (
|
||||||
|
payload: ResolveDomainCached['payload']
|
||||||
|
): ResolveDomainCached => ({
|
||||||
|
type: TypeKeys.ENS_RESOLVE_DOMAIN_CACHED,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TResolveDomainSucceeded = typeof resolveDomainSucceeded;
|
||||||
|
export const resolveDomainSucceeded = (
|
||||||
|
domain: string,
|
||||||
|
domainData: DomainRequest
|
||||||
|
): ActionTypes.ResolveDomainSucceeded => ({
|
||||||
|
type: TypeKeys.ENS_RESOLVE_DOMAIN_SUCCEEDED,
|
||||||
|
payload: { domain, domainData }
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TResolveDomainFailed = typeof resolveDomainFailed;
|
||||||
|
export const resolveDomainFailed = (
|
||||||
|
domain: string,
|
||||||
|
error: Error
|
||||||
|
): ActionTypes.ResolveDomainFailed => ({
|
||||||
|
type: TypeKeys.ENS_RESOLVE_DOMAIN_FAILED,
|
||||||
|
payload: { domain, error }
|
||||||
|
});
|
|
@ -1,17 +0,0 @@
|
||||||
/*** Resolve ENS name ***/
|
|
||||||
export interface ResolveEnsNameAction {
|
|
||||||
type: 'ENS_RESOLVE';
|
|
||||||
payload: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** Cache ENS address ***/
|
|
||||||
export interface CacheEnsAddressAction {
|
|
||||||
type: 'ENS_CACHE';
|
|
||||||
payload: {
|
|
||||||
ensName: string;
|
|
||||||
address: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** Union Type ***/
|
|
||||||
export type EnsAction = ResolveEnsNameAction | CacheEnsAddressAction;
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { ResolveDomainAction } from './resolveDomain';
|
||||||
|
|
||||||
|
export * from './resolveDomain';
|
||||||
|
|
||||||
|
export type EnsAction = ResolveDomainAction;
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './actionTypes';
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { TypeKeys } from '../constants';
|
||||||
|
import { DomainRequest } from 'libs/ens';
|
||||||
|
|
||||||
|
export interface ResolveDomainRequested {
|
||||||
|
type: TypeKeys.ENS_RESOLVE_DOMAIN_REQUESTED;
|
||||||
|
payload: { domain: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResolveDomainSucceeded {
|
||||||
|
type: TypeKeys.ENS_RESOLVE_DOMAIN_SUCCEEDED;
|
||||||
|
payload: { domain: string; domainData: DomainRequest };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResolveDomainCached {
|
||||||
|
type: TypeKeys.ENS_RESOLVE_DOMAIN_CACHED;
|
||||||
|
payload: { domain: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResolveDomainFailed {
|
||||||
|
type: TypeKeys.ENS_RESOLVE_DOMAIN_FAILED;
|
||||||
|
payload: { domain: string; error: Error };
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ResolveDomainAction =
|
||||||
|
| ResolveDomainRequested
|
||||||
|
| ResolveDomainSucceeded
|
||||||
|
| ResolveDomainFailed
|
||||||
|
| ResolveDomainCached;
|
|
@ -1,2 +1,6 @@
|
||||||
export const ENS_RESOLVE = 'ENS_RESOLVE';
|
export enum TypeKeys {
|
||||||
export const ENS_CACHE = 'ENS_CACHE';
|
ENS_RESOLVE_DOMAIN_REQUESTED = 'ENS_RESOLVE_DOMAIN_REQUESTED',
|
||||||
|
ENS_RESOLVE_DOMAIN_SUCCEEDED = 'ENS_RESOLVE_DOMAIN_SUCCEEDED',
|
||||||
|
ENS_RESOLVE_DOMAIN_CACHED = 'ENS_RESOLVE_DOMAIN_CACHED',
|
||||||
|
ENS_RESOLVE_DOMAIN_FAILED = 'ENS_RESOLVE_DOMAIN_FAILED'
|
||||||
|
}
|
||||||
|
|
|
@ -52,7 +52,6 @@ interface SetToFieldAction {
|
||||||
payload: {
|
payload: {
|
||||||
raw: string;
|
raw: string;
|
||||||
value: Address | null;
|
value: Address | null;
|
||||||
error?: string | null;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ interface SetTokenToMetaAction {
|
||||||
payload: {
|
payload: {
|
||||||
raw: string;
|
raw: string;
|
||||||
value: Address | null;
|
value: Address | null;
|
||||||
error?: string | null;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ interface Props {
|
||||||
|
|
||||||
export const AddressField: React.SFC<Props> = ({ isReadOnly }) => (
|
export const AddressField: React.SFC<Props> = ({ isReadOnly }) => (
|
||||||
<AddressFieldFactory
|
<AddressFieldFactory
|
||||||
withProps={({ currentTo, isValid, onChange, readOnly, errorMsg }) => (
|
withProps={({ currentTo, isValid, onChange, readOnly }) => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<input
|
<input
|
||||||
className={`form-control ${isValid ? 'is-valid' : 'is-invalid'}`}
|
className={`form-control ${isValid ? 'is-valid' : 'is-invalid'}`}
|
||||||
|
@ -18,11 +18,6 @@ export const AddressField: React.SFC<Props> = ({ isReadOnly }) => (
|
||||||
readOnly={!!(isReadOnly || readOnly)}
|
readOnly={!!(isReadOnly || readOnly)}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
{errorMsg && (
|
|
||||||
<div className="has-error">
|
|
||||||
<span className="help-block">{errorMsg}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -18,14 +18,12 @@ export interface CallbackProps {
|
||||||
isValid: boolean;
|
isValid: boolean;
|
||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
currentTo: ICurrentTo;
|
currentTo: ICurrentTo;
|
||||||
errorMsg?: string | null;
|
|
||||||
onChange(ev: React.FormEvent<HTMLInputElement>): void;
|
onChange(ev: React.FormEvent<HTMLInputElement>): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = DispatchProps & DispatchProps & OwnProps;
|
type Props = DispatchProps & OwnProps;
|
||||||
|
|
||||||
//TODO: add ens resolving
|
class AddressFieldFactoryClass extends React.Component<Props> {
|
||||||
class AddressFieldFactoryClass extends React.Component<Props, {}> {
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
// this 'to' parameter can be either token or actual field related
|
// this 'to' parameter can be either token or actual field related
|
||||||
const { to } = this.props;
|
const { to } = this.props;
|
||||||
|
@ -44,14 +42,17 @@ class AddressFieldFactoryClass extends React.Component<Props, {}> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddressField = connect(null, { setCurrentTo })(AddressFieldFactoryClass);
|
const AddressFieldFactory = connect(null, { setCurrentTo })(AddressFieldFactoryClass);
|
||||||
|
|
||||||
interface DefaultAddressFieldProps {
|
interface DefaultAddressFieldProps {
|
||||||
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultAddressField: React.SFC<DefaultAddressFieldProps> = ({ withProps }) => (
|
const DefaultAddressField: React.SFC<DefaultAddressFieldProps> = ({ withProps }) => (
|
||||||
<Query params={['to']} withQuery={({ to }) => <AddressField to={to} withProps={withProps} />} />
|
<Query
|
||||||
|
params={['to']}
|
||||||
|
withQuery={({ to }) => <AddressFieldFactory to={to} withProps={withProps} />}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
export { DefaultAddressField as AddressFieldFactory };
|
export { DefaultAddressField as AddressFieldFactory };
|
||||||
|
|
|
@ -1,29 +1,51 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Identicon } from 'components/ui';
|
import { Identicon, Spinner } from 'components/ui';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
//import { EnsAddress } from './components';
|
|
||||||
import { Query } from 'components/renderCbs';
|
import { Query } from 'components/renderCbs';
|
||||||
import { ICurrentTo, getCurrentTo, isValidCurrentTo } from 'selectors/transaction';
|
import { ICurrentTo, getCurrentTo, isValidCurrentTo } from 'selectors/transaction';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { CallbackProps } from 'components/AddressFieldFactory';
|
import { CallbackProps } from 'components/AddressFieldFactory';
|
||||||
|
import { addHexPrefix } from 'ethereumjs-util';
|
||||||
|
import { getResolvingDomain } from 'selectors/ens';
|
||||||
|
import { isValidENSAddress } from 'libs/validators';
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
currentTo: ICurrentTo;
|
currentTo: ICurrentTo;
|
||||||
isValid: boolean;
|
isValid: boolean;
|
||||||
|
isResolving: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
onChange(ev: React.FormEvent<HTMLInputElement>): void;
|
onChange(ev: React.FormEvent<HTMLInputElement>): void;
|
||||||
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ENSStatus: React.SFC<{ isLoading: boolean; ensAddress: string; rawAddress: string }> = ({
|
||||||
|
isLoading,
|
||||||
|
ensAddress,
|
||||||
|
rawAddress
|
||||||
|
}) => {
|
||||||
|
const isENS = isValidENSAddress(ensAddress);
|
||||||
|
const text = 'Loading ENS address...';
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Spinner /> {text}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return isENS ? <>{`Resolved Address: ${rawAddress}`}</> : null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
type Props = OwnProps & StateProps;
|
type Props = OwnProps & StateProps;
|
||||||
|
|
||||||
//TODO: ENS handling
|
|
||||||
class AddressInputFactoryClass extends Component<Props> {
|
class AddressInputFactoryClass extends Component<Props> {
|
||||||
public render() {
|
public render() {
|
||||||
const { currentTo, onChange, isValid, withProps } = this.props;
|
const { currentTo, onChange, isValid, withProps, isResolving } = this.props;
|
||||||
const { raw } = currentTo;
|
const { value } = currentTo;
|
||||||
|
const addr = addHexPrefix(value ? value.toString('hex') : '0');
|
||||||
return (
|
return (
|
||||||
<div className="row form-group">
|
<div className="row form-group">
|
||||||
<div className="col-xs-11">
|
<div className="col-xs-11">
|
||||||
|
@ -35,15 +57,14 @@ class AddressInputFactoryClass extends Component<Props> {
|
||||||
currentTo,
|
currentTo,
|
||||||
isValid,
|
isValid,
|
||||||
onChange,
|
onChange,
|
||||||
readOnly: !!readOnly,
|
readOnly: !!readOnly || this.props.isResolving
|
||||||
errorMsg: currentTo.error
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{/*<EnsAddress ensAddress={ensAddress} />*/}
|
<ENSStatus ensAddress={currentTo.raw} isLoading={isResolving} rawAddress={addr} />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-xs-1" style={{ padding: 0 }}>
|
<div className="col-xs-1" style={{ padding: 0 }}>
|
||||||
<Identicon address={/*ensAddress ||*/ raw} />
|
<Identicon address={addr} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -52,5 +73,6 @@ class AddressInputFactoryClass extends Component<Props> {
|
||||||
|
|
||||||
export const AddressInputFactory = connect((state: AppState) => ({
|
export const AddressInputFactory = connect((state: AppState) => ({
|
||||||
currentTo: getCurrentTo(state),
|
currentTo: getCurrentTo(state),
|
||||||
|
isResolving: getResolvingDomain(state),
|
||||||
isValid: isValidCurrentTo(state)
|
isValid: isValidCurrentTo(state)
|
||||||
}))(AddressInputFactoryClass);
|
}))(AddressInputFactoryClass);
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
/*
|
|
||||||
|
|
||||||
public onChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
|
|
||||||
const newValue = (e.target as HTMLInputElement).value;
|
|
||||||
const { onChange } = this.props;
|
|
||||||
if (!onChange) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// FIXME debounce?
|
|
||||||
if (isValidENSAddress(newValue)) {
|
|
||||||
this.props.resolveEnsName(newValue);
|
|
||||||
}
|
|
||||||
onChange(newValue);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function mapStateToProps(state: AppState, props: PublicProps) {
|
|
||||||
return {
|
|
||||||
ensAddress: getEnsAddress(state, props.value)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export default connect(mapStateToProps, { resolveEnsName })(AddressField);
|
|
||||||
*/
|
|
||||||
|
|
||||||
interface EnsAddressProps {
|
|
||||||
ensAddress: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EnsAddress: React.SFC<EnsAddressProps> = ({ ensAddress }) =>
|
|
||||||
(!!ensAddress && (
|
|
||||||
<p className="ens-response">
|
|
||||||
↳
|
|
||||||
<span className="mono">{ensAddress}</span>
|
|
||||||
</p>
|
|
||||||
)) ||
|
|
||||||
null;
|
|
|
@ -1 +0,0 @@
|
||||||
export * from './EnsAddress';
|
|
|
@ -142,13 +142,6 @@
|
||||||
font-size: $font-size-small;
|
font-size: $font-size-small;
|
||||||
margin: $space-sm 0;
|
margin: $space-sm 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $grid-float-breakpoint) {
|
|
||||||
.row {
|
|
||||||
margin-left: -0.5rem;
|
|
||||||
margin-right: -0.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.Modal {
|
.Modal {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { toTokenBase } from 'libs/units';
|
import { toTokenBase } from 'libs/units';
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import { getDecimal } from 'selectors/transaction';
|
||||||
|
|
||||||
interface IChildren {
|
interface IChildren {
|
||||||
onUserInput: UnitConverter['onUserInput'];
|
onUserInput: UnitConverterClass['onUserInput'];
|
||||||
convertedUnit: string;
|
convertedUnit: string;
|
||||||
}
|
}
|
||||||
interface IFakeEvent {
|
interface IFakeEvent {
|
||||||
|
@ -24,7 +26,7 @@ interface State {
|
||||||
|
|
||||||
const initialState = { userInput: '' };
|
const initialState = { userInput: '' };
|
||||||
|
|
||||||
export class UnitConverter extends Component<Props, State> {
|
class UnitConverterClass extends Component<Props, State> {
|
||||||
public state: State = initialState;
|
public state: State = initialState;
|
||||||
|
|
||||||
public componentWillReceiveProps(nextProps: Props) {
|
public componentWillReceiveProps(nextProps: Props) {
|
||||||
|
@ -58,3 +60,11 @@ export class UnitConverter extends Component<Props, State> {
|
||||||
this.props.onChange(fakeEvent);
|
this.props.onChange(fakeEvent);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state: AppState) => {
|
||||||
|
return {
|
||||||
|
decimal: getDecimal(state)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UnitConverter = connect(mapStateToProps)(UnitConverterClass);
|
||||||
|
|
|
@ -10,9 +10,7 @@ interface Props {
|
||||||
export default function Identicon(props: Props) {
|
export default function Identicon(props: Props) {
|
||||||
const size = props.size || '4rem';
|
const size = props.size || '4rem';
|
||||||
// FIXME breaks on failed checksums
|
// FIXME breaks on failed checksums
|
||||||
const identiconDataUrl = isValidETHAddress(props.address.toLowerCase())
|
const identiconDataUrl = isValidETHAddress(props.address) ? toDataUrl(props.address) : '';
|
||||||
? toDataUrl(props.address.toLowerCase())
|
|
||||||
: '';
|
|
||||||
return (
|
return (
|
||||||
<div style={{ position: 'relative', width: size, height: size }} title="Address Identicon">
|
<div style={{ position: 'relative', width: size, height: size }} title="Address Identicon">
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -6,7 +6,6 @@ const ABIFUNC_METHOD_NAMES = ['encodeInput', 'decodeInput', 'decodeOutput'];
|
||||||
enum ABIMethodTypes {
|
enum ABIMethodTypes {
|
||||||
FUNC = 'function'
|
FUNC = 'function'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TContract = typeof Contract;
|
export type TContract = typeof Contract;
|
||||||
|
|
||||||
export default class Contract {
|
export default class Contract {
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import uts46 from 'idna-uts46';
|
|
||||||
|
|
||||||
export function normalise(name: string): string {
|
|
||||||
try {
|
|
||||||
return uts46.toUnicode(name, { useStd3ASCII: true, transitional: false });
|
|
||||||
} catch (e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
export interface ABIFunc<T, K = void> {
|
||||||
|
outputType: K;
|
||||||
|
|
||||||
|
encodeInput(x: T): string;
|
||||||
|
decodeOutput(argStr: string): K;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ABIFuncParamless<T = void> {
|
||||||
|
outputType: T;
|
||||||
|
encodeInput(): string;
|
||||||
|
decodeOutput(argStr: string): T;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { ABIFunc, ABIFuncParamless } from '../AbiFunc';
|
||||||
|
export interface IAuction {
|
||||||
|
releaseDeed: ABIFunc<{ _hash: bytes32 }>;
|
||||||
|
getAllowedTime: ABIFunc<{ _hash: bytes32 }, { timestamp: uint256 }>;
|
||||||
|
invalidateName: ABIFunc<{ unhashedName: string }>;
|
||||||
|
shaBid: ABIFunc<
|
||||||
|
{ hash: bytes32; owner: address; value: uint256; salt: bytes32 },
|
||||||
|
{ sealedBid: bytes32 }
|
||||||
|
>;
|
||||||
|
cancelBid: ABIFunc<{ bidder: address; seal: bytes32 }>;
|
||||||
|
entries: ABIFunc<
|
||||||
|
{ _hash: bytes32 },
|
||||||
|
{
|
||||||
|
mode: uint8;
|
||||||
|
deedAddress: address;
|
||||||
|
registrationDate: uint256;
|
||||||
|
value: uint256;
|
||||||
|
highestBid: uint256;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
ens: ABIFuncParamless<{ ensAddress: address }>;
|
||||||
|
unsealBid: ABIFunc<{ _hash: bytes32; _value: uint256; _salt: bytes32 }>;
|
||||||
|
transferRegistrars: ABIFunc<{ _hash: bytes32 }>;
|
||||||
|
sealedBids: ABIFunc<{ address_0: address; bytes32_1: bytes32 }, { deedAddress: address }>;
|
||||||
|
state: ABIFunc<{ _hash: bytes32 }, { state: uint8 }>;
|
||||||
|
transfer: ABIFunc<{ _hash: bytes32; newOwner: address }>;
|
||||||
|
isAllowed: ABIFunc<{ _hash: bytes32; _timestamp: uint256 }, { allowed: bool }>;
|
||||||
|
finalizeAuction: ABIFunc<{ _hash: bytes32 }>;
|
||||||
|
registryStarted: ABIFuncParamless<{ registryStartDate: uint256 }>;
|
||||||
|
launchLength: ABIFuncParamless<{ launchLength: uint32 }>;
|
||||||
|
newBid: ABIFunc<{ sealedBid: bytes32 }>;
|
||||||
|
eraseNode: ABIFunc<{ labels: bytes32[] }>;
|
||||||
|
startAuctions: ABIFunc<{ _hashes: bytes32[] }>;
|
||||||
|
acceptRegistrarTransfer: ABIFunc<{
|
||||||
|
hash: bytes32;
|
||||||
|
deed: address;
|
||||||
|
registrationDate: uint256;
|
||||||
|
}>;
|
||||||
|
startAuction: ABIFunc<{ _hash: bytes32 }>;
|
||||||
|
rootNode: ABIFuncParamless<{ rootNode: bytes32 }>;
|
||||||
|
startAuctionsAndBid: ABIFunc<{ hashes: bytes32[]; sealedBid: bytes32 }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type bytes32 = any;
|
||||||
|
type uint256 = any;
|
||||||
|
type address = any;
|
||||||
|
type uint8 = any;
|
||||||
|
type bool = boolean;
|
||||||
|
type uint32 = any;
|
|
@ -0,0 +1,550 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "releaseDeed",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "getAllowedTime",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "timestamp",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "unhashedName",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "invalidateName",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "salt",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "shaBid",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "sealedBid",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "bidder",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "seal",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "cancelBid",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "entries",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "ens",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_salt",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "unsealBid",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "transferRegistrars",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "sealedBids",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "state",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "newOwner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "transfer",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_timestamp",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "isAllowed",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "allowed",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "finalizeAuction",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "registryStarted",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "launchLength",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "sealedBid",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "newBid",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": true,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "labels",
|
||||||
|
"type": "bytes32[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "eraseNode",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_hashes",
|
||||||
|
"type": "bytes32[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "startAuctions",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "deed",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "registrationDate",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "acceptRegistrarTransfer",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "startAuction",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "rootNode",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "hashes",
|
||||||
|
"type": "bytes32[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sealedBid",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "startAuctionsAndBid",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": true,
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_ens",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_rootNode",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_startDate",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"type": "constructor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "registrationDate",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "AuctionStarted",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "bidder",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "deposit",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "NewBid",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "status",
|
||||||
|
"type": "uint8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "BidRevealed",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "registrationDate",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "HashRegistered",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "HashReleased",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "value",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "registrationDate",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "HashInvalidated",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,9 @@
|
||||||
|
export default {
|
||||||
|
ens: ['ensAddress'],
|
||||||
|
entries: ['mode', 'deedAddress', 'registrationDate', 'value', 'highestBid'],
|
||||||
|
sealedBids: ['deedAddress'],
|
||||||
|
state: ['state'],
|
||||||
|
registryStarted: ['registryStartDate'],
|
||||||
|
launchLength: ['launchLength'],
|
||||||
|
rootNode: ['rootNode']
|
||||||
|
};
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { ABIFunc, ABIFuncParamless } from '../AbiFunc';
|
||||||
|
|
||||||
|
export interface IDeed {
|
||||||
|
creationDate: ABIFuncParamless<{ creationDate: uint256 }>;
|
||||||
|
destroyDeed: ABIFuncParamless;
|
||||||
|
setOwner: ABIFunc<{ newOwner: address }>;
|
||||||
|
registrar: ABIFuncParamless<{ registrarAddress: address }>;
|
||||||
|
owner: ABIFuncParamless<{ ownerAddress: address }>;
|
||||||
|
closeDeed: ABIFunc<{ refundRatio: uint256 }>;
|
||||||
|
setRegistrar: ABIFunc<{ newRegistrar: address }>;
|
||||||
|
setBalance: ABIFunc<{ newValue: uint256 }>;
|
||||||
|
}
|
||||||
|
type uint256 = any;
|
||||||
|
type address = any;
|
|
@ -0,0 +1,98 @@
|
||||||
|
[{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "creationDate",
|
||||||
|
"outputs": [{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "destroyDeed",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "newOwner",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"name": "setOwner",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "registrar",
|
||||||
|
"outputs": [{
|
||||||
|
"name": "",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "owner",
|
||||||
|
"outputs": [{
|
||||||
|
"name": "",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "refundRatio",
|
||||||
|
"type": "uint256"
|
||||||
|
}],
|
||||||
|
"name": "closeDeed",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "newRegistrar",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"name": "setRegistrar",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "newValue",
|
||||||
|
"type": "uint256"
|
||||||
|
}],
|
||||||
|
"name": "setBalance",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": true,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"inputs": [],
|
||||||
|
"type": "constructor"
|
||||||
|
}, {
|
||||||
|
"payable": true,
|
||||||
|
"type": "fallback"
|
||||||
|
}, {
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "newOwner",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"name": "OwnerChanged",
|
||||||
|
"type": "event"
|
||||||
|
}, {
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "DeedClosed",
|
||||||
|
"type": "event"
|
||||||
|
}]
|
|
@ -0,0 +1,5 @@
|
||||||
|
export default {
|
||||||
|
creationDate: ['creationDate'],
|
||||||
|
registrar: ['registrarAddress'],
|
||||||
|
owner: ['ownerAddress']
|
||||||
|
};
|
|
@ -0,0 +1,26 @@
|
||||||
|
import Contract from 'libs/contracts';
|
||||||
|
const auctionABI = require('./auction/auction.json');
|
||||||
|
import auctionOutputMappings from './auction/outputMappings';
|
||||||
|
import { IAuction } from './auction/auction';
|
||||||
|
|
||||||
|
const deedABI = require('./deed/deed.json');
|
||||||
|
import deedOutputMappings from './deed/outputMappings';
|
||||||
|
import { IDeed } from './deed/deed';
|
||||||
|
|
||||||
|
const registryABI = require('./registry/registry.json');
|
||||||
|
import registryOutputMappings from './registry/outputMappings';
|
||||||
|
import { IRegistry } from './registry/registry';
|
||||||
|
|
||||||
|
const resolverABI = require('./resolver/resolver.json');
|
||||||
|
import resolverOutputMappings from './resolver/outputMappings';
|
||||||
|
import { IResolver } from './resolver/resolver';
|
||||||
|
|
||||||
|
const auction: IAuction & Contract = new Contract(auctionABI, auctionOutputMappings) as any;
|
||||||
|
|
||||||
|
const deed: IDeed & Contract = new Contract(deedABI, deedOutputMappings) as any;
|
||||||
|
|
||||||
|
const registry: IRegistry & Contract = new Contract(registryABI, registryOutputMappings) as any;
|
||||||
|
|
||||||
|
const resolver: IResolver & Contract = new Contract(resolverABI, resolverOutputMappings) as any;
|
||||||
|
|
||||||
|
export default { auction, deed, registry, resolver };
|
|
@ -0,0 +1,5 @@
|
||||||
|
export default {
|
||||||
|
resolver: ['resolverAddress'],
|
||||||
|
owner: ['ownerAddress'],
|
||||||
|
ttl: ['timeToLive']
|
||||||
|
};
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { ABIFunc, ABIFuncParamless } from '../AbiFunc';
|
||||||
|
|
||||||
|
export interface IRegistry {
|
||||||
|
resolver: ABIFunc<{ node: bytes32 }, { resolverAddress: address }>;
|
||||||
|
owner: ABIFunc<{ node: bytes32 }, { ownerAddress: address }>;
|
||||||
|
setSubnodeOwner: ABIFunc<{ node: bytes32; label: bytes32; owner: address }>;
|
||||||
|
setTTL: ABIFunc<{ node: bytes32; ttl: uint64 }>;
|
||||||
|
ttl: ABIFunc<{ node: bytes32 }, { timeToLive: uint64 }>;
|
||||||
|
setResolver: ABIFunc<{ node: bytes32; resolver: address }>;
|
||||||
|
setOwner: ABIFunc<{ node: bytes32; owner: address }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type bytes32 = any;
|
||||||
|
type address = any;
|
||||||
|
type uint64 = any;
|
|
@ -0,0 +1,151 @@
|
||||||
|
[{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}],
|
||||||
|
"name": "resolver",
|
||||||
|
"outputs": [{
|
||||||
|
"name": "",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}],
|
||||||
|
"name": "owner",
|
||||||
|
"outputs": [{
|
||||||
|
"name": "",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"name": "label",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"name": "setSubnodeOwner",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"name": "ttl",
|
||||||
|
"type": "uint64"
|
||||||
|
}],
|
||||||
|
"name": "setTTL",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}],
|
||||||
|
"name": "ttl",
|
||||||
|
"outputs": [{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint64"
|
||||||
|
}],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"name": "resolver",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"name": "setResolver",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"name": "setOwner",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"indexed": false,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"name": "Transfer",
|
||||||
|
"type": "event"
|
||||||
|
}, {
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"indexed": true,
|
||||||
|
"name": "label",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"indexed": false,
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"name": "NewOwner",
|
||||||
|
"type": "event"
|
||||||
|
}, {
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"indexed": false,
|
||||||
|
"name": "resolver",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"name": "NewResolver",
|
||||||
|
"type": "event"
|
||||||
|
}, {
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"indexed": false,
|
||||||
|
"name": "ttl",
|
||||||
|
"type": "uint64"
|
||||||
|
}],
|
||||||
|
"name": "NewTTL",
|
||||||
|
"type": "event"
|
||||||
|
}]
|
|
@ -0,0 +1,4 @@
|
||||||
|
export default {
|
||||||
|
supportsInterface: ['doesSupportInterface'],
|
||||||
|
has: ['has']
|
||||||
|
};
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { ABIFunc, ABIFuncParamless } from '../AbiFunc';
|
||||||
|
|
||||||
|
export interface IResolver {
|
||||||
|
supportsInterface: ABIFunc<{ interfaceID: bytes4 }, { doesSupportInterface: bool }>;
|
||||||
|
addr: ABIFunc<{ node: bytes32 }, { ret: address }>;
|
||||||
|
has: ABIFunc<{ node: bytes32; kind: bytes32 }, { has: bool }>;
|
||||||
|
setAddr: ABIFunc<{ node: bytes32; addr: address }>;
|
||||||
|
content: ABIFunc<{ node: bytes32 }, { ret: bytes32 }>;
|
||||||
|
setContent: ABIFunc<{ node: bytes32; hash: bytes32 }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type bytes4 = any;
|
||||||
|
type bool = boolean;
|
||||||
|
type bytes32 = any;
|
||||||
|
type address = any;
|
|
@ -0,0 +1,91 @@
|
||||||
|
[{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "interfaceID",
|
||||||
|
"type": "bytes4"
|
||||||
|
}],
|
||||||
|
"name": "supportsInterface",
|
||||||
|
"outputs": [{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}],
|
||||||
|
"name": "addr",
|
||||||
|
"outputs": [{
|
||||||
|
"name": "ret",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"name": "kind",
|
||||||
|
"type": "bytes32"
|
||||||
|
}],
|
||||||
|
"name": "has",
|
||||||
|
"outputs": [{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"name": "addr",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"name": "setAddr",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}],
|
||||||
|
"name": "content",
|
||||||
|
"outputs": [{
|
||||||
|
"name": "ret",
|
||||||
|
"type": "bytes32"
|
||||||
|
}],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "node",
|
||||||
|
"type": "bytes32"
|
||||||
|
}, {
|
||||||
|
"name": "hash",
|
||||||
|
"type": "bytes32"
|
||||||
|
}],
|
||||||
|
"name": "setContent",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"type": "function"
|
||||||
|
}, {
|
||||||
|
"inputs": [{
|
||||||
|
"name": "ensAddr",
|
||||||
|
"type": "address"
|
||||||
|
}],
|
||||||
|
"type": "constructor"
|
||||||
|
}, {
|
||||||
|
"payable": false,
|
||||||
|
"type": "fallback"
|
||||||
|
}]
|
|
@ -0,0 +1,86 @@
|
||||||
|
import uts46 from 'idna-uts46';
|
||||||
|
import ethUtil from 'ethereumjs-util';
|
||||||
|
|
||||||
|
export function normalise(name: string) {
|
||||||
|
try {
|
||||||
|
return uts46.toUnicode(name, { useStd3ASCII: true, transitional: false });
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getNameHash = (name: string = ''): string => {
|
||||||
|
if (name === '') {
|
||||||
|
throw new Error('Empty string provided');
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedName = normalise(name);
|
||||||
|
const sha3 = ethUtil.sha3;
|
||||||
|
const labels = normalizedName.split('.');
|
||||||
|
const emptyNode = Buffer.alloc(32);
|
||||||
|
const rawNode = labels.reduceRight((node, currentLabel) => {
|
||||||
|
return sha3(Buffer.concat([node, sha3(currentLabel)]));
|
||||||
|
}, emptyNode);
|
||||||
|
|
||||||
|
return `0x${rawNode.toString('hex')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IBaseDomainRequest {
|
||||||
|
name: string;
|
||||||
|
labelHash: string;
|
||||||
|
mode: NameState;
|
||||||
|
highestBid: string;
|
||||||
|
value: string;
|
||||||
|
deedAddress: string;
|
||||||
|
registrationDate: string;
|
||||||
|
nameHash: string;
|
||||||
|
mappedMode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IOwnedDomainRequest extends IBaseDomainRequest {
|
||||||
|
ownerAddress: string;
|
||||||
|
resolvedAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRevealDomainRequest extends IBaseDomainRequest {
|
||||||
|
ownerAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DomainRequest = IOwnedDomainRequest | IRevealDomainRequest | IBaseDomainRequest;
|
||||||
|
|
||||||
|
export interface IDomainData<Mode> {
|
||||||
|
mode: Mode;
|
||||||
|
deedAddress: string;
|
||||||
|
registrationDate: string;
|
||||||
|
value: string;
|
||||||
|
highestBid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum NameState {
|
||||||
|
Open = '0',
|
||||||
|
Auction = '1',
|
||||||
|
Owned = '2',
|
||||||
|
Forbidden = '3',
|
||||||
|
Reveal = '4',
|
||||||
|
NotYetAvailable = '5'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const modeStrMap = name => [
|
||||||
|
`${name} is available and the auction hasn’t started`,
|
||||||
|
`${name} is available and the auction has been started`,
|
||||||
|
`${name} is taken and currently owned by someone`,
|
||||||
|
`${name} is forbidden`,
|
||||||
|
`${name} is currently in the ‘reveal’ stage of the auction`,
|
||||||
|
`${name} is not yet available due to the ‘soft launch’ of names.`
|
||||||
|
];
|
||||||
|
|
||||||
|
export interface IModeMap {
|
||||||
|
[x: string]: (
|
||||||
|
domainData: IDomainData<NameState>,
|
||||||
|
nameHash?: string,
|
||||||
|
hash?: Buffer
|
||||||
|
) =>
|
||||||
|
| {}
|
||||||
|
| { ownerAddress: string; resolvedAddress: string }
|
||||||
|
| { auctionCloseTime: string; revealBidTime: string };
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
const main: IEnsAddresses = require('./main.json');
|
||||||
|
const rinkeby: IEnsAddresses = require('./rinkeby.json');
|
||||||
|
const ropsten: IEnsAddresses = require('./ropsten.json');
|
||||||
|
|
||||||
|
interface IEnsAddresses {
|
||||||
|
public: {
|
||||||
|
resolver: string;
|
||||||
|
reverse: string;
|
||||||
|
ethAuction: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
registry: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { main, rinkeby, ropsten };
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"public": {
|
||||||
|
"resolver": "0x5FfC014343cd971B7eb70732021E26C35B744cc4",
|
||||||
|
"reverse": "0x9062c0a6dbd6108336bcbe4593a3d1ce05512069",
|
||||||
|
"ethAuction": "0x6090a6e47849629b7245dfa1ca21d94cd15878ef"
|
||||||
|
},
|
||||||
|
"registry": "0x314159265dD8dbb310642f98f50C066173C1259b"
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"public": {
|
||||||
|
"resolver": "0xb14fdee4391732ea9d2267054ead2084684c0ad8",
|
||||||
|
"reverse": "0x0000000000000000000000000000000000000000",
|
||||||
|
"ethAuction": "0x0000000000000000000000000000000000000000"
|
||||||
|
},
|
||||||
|
"registry": "0xe7410170f87102df0055eb195163a03b7f2bff4a"
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"public": {
|
||||||
|
"resolver": "0x4c641fb9bad9b60ef180c31f56051ce826d21a9a",
|
||||||
|
"reverse": "0xdb6cead81ce14a63c284728eed17738a81327ff0",
|
||||||
|
"ethAuction": "0xc19fd9004b5c9789391679de6d766b981db94610"
|
||||||
|
},
|
||||||
|
"registry": "0x112234455c3a32fd11230c42e7bccd4a84e02010"
|
||||||
|
}
|
|
@ -22,6 +22,9 @@ export function isValidETHAddress(address: string): boolean {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isCreationAddress = (address: string): boolean =>
|
||||||
|
address === '0x0' || address === '0x0000000000000000000000000000000000000000';
|
||||||
|
|
||||||
export function isValidBTCAddress(address: string): boolean {
|
export function isValidBTCAddress(address: string): boolean {
|
||||||
return WalletAddressValidator.validate(address, 'BTC');
|
return WalletAddressValidator.validate(address, 'BTC');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { CacheEnsAddressAction, EnsAction } from 'actions/ens';
|
|
||||||
|
|
||||||
export interface State {
|
|
||||||
[key: string]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const INITIAL_STATE: State = {};
|
|
||||||
|
|
||||||
function cacheEnsAddress(state: State, action: CacheEnsAddressAction): State {
|
|
||||||
const { ensName, address } = action.payload;
|
|
||||||
return { ...state, [ensName]: address };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ens(state: State = INITIAL_STATE, action: EnsAction): State {
|
|
||||||
switch (action.type) {
|
|
||||||
case 'ENS_CACHE':
|
|
||||||
return cacheEnsAddress(state, action);
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
import {
|
||||||
|
EnsAction,
|
||||||
|
ResolveDomainRequested,
|
||||||
|
ResolveDomainFailed,
|
||||||
|
ResolveDomainSucceeded,
|
||||||
|
ResolveDomainCached
|
||||||
|
} from 'actions/ens';
|
||||||
|
import { DomainRequest } from 'libs/ens';
|
||||||
|
import { TypeKeys } from 'actions/ens/constants';
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
[key: string]: {
|
||||||
|
state: REQUEST_STATES;
|
||||||
|
data?: DomainRequest;
|
||||||
|
error?: boolean;
|
||||||
|
errorMsg?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const INITIAL_STATE: State = {};
|
||||||
|
|
||||||
|
export enum REQUEST_STATES {
|
||||||
|
pending = 'PENDING',
|
||||||
|
success = 'SUCCESS',
|
||||||
|
failed = 'FAILED'
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveDomainRequested = (state: State, action: ResolveDomainRequested): State => {
|
||||||
|
const { domain } = action.payload;
|
||||||
|
const nextDomain = {
|
||||||
|
...state[domain],
|
||||||
|
state: REQUEST_STATES.pending
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...state, [domain]: nextDomain };
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveDomainSuccess = (state: State, action: ResolveDomainSucceeded): State => {
|
||||||
|
const { domain, domainData } = action.payload;
|
||||||
|
const nextDomain = {
|
||||||
|
data: domainData,
|
||||||
|
state: REQUEST_STATES.success
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...state, [domain]: nextDomain };
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveDomainCached = (state: State, action: ResolveDomainCached): State => {
|
||||||
|
const { domain } = action.payload;
|
||||||
|
const nextDomain = {
|
||||||
|
...state[domain],
|
||||||
|
state: REQUEST_STATES.success
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...state, [domain]: nextDomain };
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveDomainFailed = (state: State, action: ResolveDomainFailed): State => {
|
||||||
|
const { domain, error } = action.payload;
|
||||||
|
const nextDomain = {
|
||||||
|
error: true,
|
||||||
|
errorMsg: error.message,
|
||||||
|
state: REQUEST_STATES.failed
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...state, [domain]: nextDomain };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state: State = INITIAL_STATE, action: EnsAction): State => {
|
||||||
|
switch (action.type) {
|
||||||
|
case TypeKeys.ENS_RESOLVE_DOMAIN_REQUESTED:
|
||||||
|
return resolveDomainRequested(state, action);
|
||||||
|
case TypeKeys.ENS_RESOLVE_DOMAIN_SUCCEEDED:
|
||||||
|
return resolveDomainSuccess(state, action);
|
||||||
|
case TypeKeys.ENS_RESOLVE_DOMAIN_FAILED:
|
||||||
|
return resolveDomainFailed(state, action);
|
||||||
|
case TypeKeys.ENS_RESOLVE_DOMAIN_CACHED:
|
||||||
|
return resolveDomainCached(state, action);
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
import {
|
||||||
|
EnsAction,
|
||||||
|
ResolveDomainSucceeded,
|
||||||
|
ResolveDomainCached,
|
||||||
|
ResolveDomainRequested
|
||||||
|
} from 'actions/ens';
|
||||||
|
import { TypeKeys } from 'actions/ens/constants';
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
currentDomain: null | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const INITIAL_STATE: State = {
|
||||||
|
currentDomain: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCurrentDomainName = (
|
||||||
|
state: State,
|
||||||
|
action: ResolveDomainSucceeded | ResolveDomainCached | ResolveDomainRequested
|
||||||
|
): State => {
|
||||||
|
const { domain: domainName } = action.payload;
|
||||||
|
return { ...state, currentDomain: domainName };
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCurrentDomainName = (): State => {
|
||||||
|
return { currentDomain: null };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state: State = INITIAL_STATE, action: EnsAction): State => {
|
||||||
|
switch (action.type) {
|
||||||
|
case TypeKeys.ENS_RESOLVE_DOMAIN_CACHED:
|
||||||
|
case TypeKeys.ENS_RESOLVE_DOMAIN_REQUESTED:
|
||||||
|
case TypeKeys.ENS_RESOLVE_DOMAIN_SUCCEEDED:
|
||||||
|
return setCurrentDomainName(state, action);
|
||||||
|
case TypeKeys.ENS_RESOLVE_DOMAIN_FAILED:
|
||||||
|
return clearCurrentDomainName();
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
import domainSelector, { State as DSState } from './domainSelector';
|
||||||
|
import domainRequests, { State as DRState } from './domainRequests';
|
||||||
|
|
||||||
|
import { combineReducers } from 'redux';
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
domainSelector: DSState;
|
||||||
|
domainRequests: DRState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ens = combineReducers({ domainSelector, domainRequests });
|
|
@ -13,7 +13,7 @@ import { State } from './typings';
|
||||||
import { gasPricetoBase } from 'libs/units';
|
import { gasPricetoBase } from 'libs/units';
|
||||||
|
|
||||||
const INITIAL_STATE: State = {
|
const INITIAL_STATE: State = {
|
||||||
to: { raw: '', value: null, error: null },
|
to: { raw: '', value: null },
|
||||||
data: { raw: '', value: null },
|
data: { raw: '', value: null },
|
||||||
nonce: { raw: '', value: null },
|
nonce: { raw: '', value: null },
|
||||||
value: { raw: '', value: null },
|
value: { raw: '', value: null },
|
||||||
|
|
|
@ -18,7 +18,7 @@ const INITIAL_STATE: State = {
|
||||||
previousUnit: 'ether',
|
previousUnit: 'ether',
|
||||||
decimal: getDecimalFromEtherUnit('ether'),
|
decimal: getDecimalFromEtherUnit('ether'),
|
||||||
tokenValue: { raw: '', value: null },
|
tokenValue: { raw: '', value: null },
|
||||||
tokenTo: { raw: '', value: null, error: null },
|
tokenTo: { raw: '', value: null },
|
||||||
from: null
|
from: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
import {
|
||||||
|
resolveDomainFailed,
|
||||||
|
resolveDomainSucceeded,
|
||||||
|
ResolveDomainRequested,
|
||||||
|
resolveDomainCached
|
||||||
|
} from 'actions/ens';
|
||||||
|
import { TypeKeys } from 'actions/ens/constants';
|
||||||
|
import { SagaIterator, delay, buffers } from 'redux-saga';
|
||||||
|
import { INode } from 'libs/nodes/INode';
|
||||||
|
import { getNodeLib } from 'selectors/config';
|
||||||
|
import { call, put, select, all, actionChannel, take, fork, race } from 'redux-saga/effects';
|
||||||
|
import { showNotification } from 'actions/notifications';
|
||||||
|
import { resolveDomainRequest } from './modeMap';
|
||||||
|
import { getCurrentDomainName, getCurrentDomainData } from 'selectors/ens';
|
||||||
|
|
||||||
|
function* shouldResolveDomain(domain: string) {
|
||||||
|
const currentDomainName = yield select(getCurrentDomainName);
|
||||||
|
if (currentDomainName === domain) {
|
||||||
|
const currentDomainData = yield select(getCurrentDomainData);
|
||||||
|
if (currentDomainData) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function* resolveDomain(): SagaIterator {
|
||||||
|
const requestChan = yield actionChannel(
|
||||||
|
TypeKeys.ENS_RESOLVE_DOMAIN_REQUESTED,
|
||||||
|
buffers.sliding(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { payload }: ResolveDomainRequested = yield take(requestChan);
|
||||||
|
|
||||||
|
const { domain } = payload;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const shouldResolve = yield call(shouldResolveDomain, domain);
|
||||||
|
if (!shouldResolve) {
|
||||||
|
yield put(resolveDomainCached({ domain }));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const node: INode = yield select(getNodeLib);
|
||||||
|
const result = yield race({
|
||||||
|
domainData: call(resolveDomainRequest, domain, node),
|
||||||
|
err: call(delay, 4000)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { domainData } = result;
|
||||||
|
if (!domainData) {
|
||||||
|
throw Error();
|
||||||
|
}
|
||||||
|
const domainSuccessAction = resolveDomainSucceeded(domain, domainData);
|
||||||
|
yield put(domainSuccessAction);
|
||||||
|
yield;
|
||||||
|
} catch (e) {
|
||||||
|
const domainFailAction = resolveDomainFailed(domain, e);
|
||||||
|
yield put(domainFailAction);
|
||||||
|
yield put(showNotification('danger', e.message || 'Could not resolve ENS address', 5000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* ens(): SagaIterator {
|
||||||
|
yield all([fork(resolveDomain)]);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { select, apply, call } from 'redux-saga/effects';
|
||||||
|
import { INode } from 'libs/nodes/INode';
|
||||||
|
import { getNodeLib } from 'selectors/config';
|
||||||
|
import { SagaIterator } from 'redux-saga';
|
||||||
|
|
||||||
|
export function* makeEthCallAndDecode({ to, data, decoder }): SagaIterator {
|
||||||
|
const node: INode = yield select(getNodeLib);
|
||||||
|
const result: string = yield apply(node, node.sendCallRequest, [{ data, to }]);
|
||||||
|
const decodedResult = yield call(decoder, result);
|
||||||
|
return decodedResult;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './ens';
|
|
@ -0,0 +1,93 @@
|
||||||
|
import { IDomainData, NameState, getNameHash } from 'libs/ens';
|
||||||
|
import ENS from 'libs/ens/contracts';
|
||||||
|
import { SagaIterator } from 'redux-saga';
|
||||||
|
import { call } from 'redux-saga/effects';
|
||||||
|
import networkConfigs from 'libs/ens/networkConfigs';
|
||||||
|
import { makeEthCallAndDecode } from 'sagas/ens/helpers';
|
||||||
|
import ethUtil from 'ethereumjs-util';
|
||||||
|
|
||||||
|
const { main } = networkConfigs;
|
||||||
|
|
||||||
|
function* nameStateOwned({ deedAddress }: IDomainData<NameState.Owned>, nameHash: string) {
|
||||||
|
// Return the owner's address, and the resolved address if it exists
|
||||||
|
const { ownerAddress }: typeof ENS.deed.owner.outputType = yield call(makeEthCallAndDecode, {
|
||||||
|
to: deedAddress,
|
||||||
|
data: ENS.deed.owner.encodeInput(),
|
||||||
|
decoder: ENS.deed.owner.decodeOutput
|
||||||
|
});
|
||||||
|
|
||||||
|
const { resolverAddress }: typeof ENS.registry.resolver.outputType = yield call(
|
||||||
|
makeEthCallAndDecode,
|
||||||
|
{
|
||||||
|
to: main.registry,
|
||||||
|
decoder: ENS.registry.resolver.decodeOutput,
|
||||||
|
data: ENS.registry.resolver.encodeInput({
|
||||||
|
node: nameHash
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let resolvedAddress = '0x0';
|
||||||
|
|
||||||
|
if (resolverAddress !== '0x0') {
|
||||||
|
const result: typeof ENS.resolver.addr.outputType = yield call(makeEthCallAndDecode, {
|
||||||
|
to: resolverAddress,
|
||||||
|
data: ENS.resolver.addr.encodeInput({ node: nameHash }),
|
||||||
|
decoder: ENS.resolver.addr.decodeOutput
|
||||||
|
});
|
||||||
|
|
||||||
|
resolvedAddress = result.ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ownerAddress, resolvedAddress };
|
||||||
|
}
|
||||||
|
|
||||||
|
function* nameStateReveal({ deedAddress }: IDomainData<NameState.Reveal>): SagaIterator {
|
||||||
|
const { ownerAddress }: typeof ENS.deed.owner.outputType = yield call(makeEthCallAndDecode, {
|
||||||
|
to: deedAddress,
|
||||||
|
data: ENS.deed.owner.encodeInput(),
|
||||||
|
decoder: ENS.deed.owner.decodeOutput
|
||||||
|
});
|
||||||
|
return ownerAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IModeMap {
|
||||||
|
[x: string]: (
|
||||||
|
domainData: IDomainData<NameState>,
|
||||||
|
nameHash?: string,
|
||||||
|
hash?: Buffer
|
||||||
|
) =>
|
||||||
|
| {}
|
||||||
|
| { ownerAddress: string; resolvedAddress: string }
|
||||||
|
| { auctionCloseTime: string; revealBidTime: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
const modeMap: IModeMap = {
|
||||||
|
[NameState.Open]: (_: IDomainData<NameState.Open>) => ({}),
|
||||||
|
[NameState.Auction]: (_: IDomainData<NameState.Auction>) => ({}),
|
||||||
|
[NameState.Owned]: nameStateOwned,
|
||||||
|
[NameState.Forbidden]: (_: IDomainData<NameState.Forbidden>) => ({}),
|
||||||
|
[NameState.Reveal]: nameStateReveal,
|
||||||
|
[NameState.NotYetAvailable]: (_: IDomainData<NameState.NotYetAvailable>) => ({})
|
||||||
|
};
|
||||||
|
|
||||||
|
export function* resolveDomainRequest(name: string): SagaIterator {
|
||||||
|
const hash = ethUtil.sha3(name);
|
||||||
|
const nameHash = getNameHash(`${name}.eth`);
|
||||||
|
|
||||||
|
const domainData: typeof ENS.auction.entries.outputType = yield call(makeEthCallAndDecode, {
|
||||||
|
to: main.public.ethAuction,
|
||||||
|
data: ENS.auction.entries.encodeInput({ _hash: hash }),
|
||||||
|
decoder: ENS.auction.entries.decodeOutput
|
||||||
|
});
|
||||||
|
const nameStateHandler = modeMap[domainData.mode];
|
||||||
|
const result = yield call(nameStateHandler, domainData, nameHash);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
...domainData,
|
||||||
|
...result,
|
||||||
|
labelHash: hash.toString('hex'),
|
||||||
|
nameHash
|
||||||
|
};
|
||||||
|
}
|
|
@ -12,9 +12,11 @@ import {
|
||||||
import { liteSend } from './swap/liteSend';
|
import { liteSend } from './swap/liteSend';
|
||||||
import { getBityRatesSaga, getShapeShiftRatesSaga, swapProviderSaga } from './swap/rates';
|
import { getBityRatesSaga, getShapeShiftRatesSaga, swapProviderSaga } from './swap/rates';
|
||||||
import wallet from './wallet';
|
import wallet from './wallet';
|
||||||
|
import { ens } from './ens';
|
||||||
import { transaction } from './transaction';
|
import { transaction } from './transaction';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
ens,
|
||||||
liteSend,
|
liteSend,
|
||||||
configSaga,
|
configSaga,
|
||||||
postBityOrderSaga,
|
postBityOrderSaga,
|
||||||
|
|
|
@ -3,26 +3,43 @@ import { SetCurrentToAction } from 'actions/transaction/actionTypes/current';
|
||||||
import { setToField } from 'actions/transaction/actionCreators/fields';
|
import { setToField } from 'actions/transaction/actionCreators/fields';
|
||||||
import { setTokenTo } from 'actions/transaction/actionCreators/meta';
|
import { setTokenTo } from 'actions/transaction/actionCreators/meta';
|
||||||
import { Address } from 'libs/units';
|
import { Address } from 'libs/units';
|
||||||
import { select, call, put, takeLatest } from 'redux-saga/effects';
|
import { select, call, put, takeLatest, take } from 'redux-saga/effects';
|
||||||
import { SagaIterator } from 'redux-saga';
|
import { SagaIterator } from 'redux-saga';
|
||||||
import { isValidENSAddress, isValidETHAddress } from 'libs/validators';
|
import { isValidENSAddress, isValidETHAddress } from 'libs/validators';
|
||||||
import { TypeKeys } from 'actions/transaction/constants';
|
import { TypeKeys } from 'actions/transaction/constants';
|
||||||
|
import { getResolvedAddress } from 'selectors/ens';
|
||||||
|
import { resolveDomainRequested, TypeKeys as ENSTypekeys } from 'actions/ens';
|
||||||
|
import { SetToFieldAction, SetTokenToMetaAction } from 'actions/transaction';
|
||||||
|
|
||||||
export function* setCurrentTo({ payload: raw }: SetCurrentToAction): SagaIterator {
|
export function* setCurrentTo({ payload: raw }: SetCurrentToAction): SagaIterator {
|
||||||
const validAddress: boolean = yield call(isValidETHAddress, raw);
|
const validAddress: boolean = yield call(isValidETHAddress, raw);
|
||||||
const validEns: boolean = yield call(isValidENSAddress, raw);
|
const validEns: boolean = yield call(isValidENSAddress, raw);
|
||||||
const etherTransaction: boolean = yield select(isEtherTransaction);
|
|
||||||
|
|
||||||
let value: Buffer | null = null;
|
let value: Buffer | null = null;
|
||||||
let error: string | null = null;
|
|
||||||
if (validAddress) {
|
if (validAddress) {
|
||||||
value = Address(raw);
|
value = Address(raw);
|
||||||
} else if (validEns) {
|
} else if (validEns) {
|
||||||
// TODO: Resolve ENS on networks that support it, error on ones that don't
|
yield call(setField, { value, raw });
|
||||||
error = 'ENS is not supported yet';
|
|
||||||
|
const [domain] = raw.split('.');
|
||||||
|
yield put(resolveDomainRequested(domain));
|
||||||
|
yield take([
|
||||||
|
ENSTypekeys.ENS_RESOLVE_DOMAIN_FAILED,
|
||||||
|
ENSTypekeys.ENS_RESOLVE_DOMAIN_SUCCEEDED,
|
||||||
|
ENSTypekeys.ENS_RESOLVE_DOMAIN_CACHED
|
||||||
|
]);
|
||||||
|
const resolvedAddress: string | null = yield select(getResolvedAddress, true);
|
||||||
|
if (resolvedAddress) {
|
||||||
|
value = Address(resolvedAddress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = { raw, value, error };
|
yield call(setField, { value, raw });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* setField(payload: SetToFieldAction['payload'] | SetTokenToMetaAction['payload']) {
|
||||||
|
const etherTransaction: boolean = yield select(isEtherTransaction);
|
||||||
|
|
||||||
if (etherTransaction) {
|
if (etherTransaction) {
|
||||||
yield put(setToField(payload));
|
yield put(setToField(payload));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
$table-cell-padding: $space-sm;
|
$table-cell-padding: $space-sm;
|
||||||
$table-condensed-cell-padding: $space-xs;
|
$table-condensed-cell-padding: $space-xs;
|
||||||
$table-bg: transparent;
|
$table-bg: transparent;
|
||||||
$table-bg-accent: #f9f9f9;
|
$table-bg-accent: #f5f5f5;
|
||||||
$table-bg-hover: $gray-lightest;
|
$table-bg-hover: $gray-lightest;
|
||||||
$table-bg-active: $table-bg-hover;
|
$table-bg-active: $table-bg-hover;
|
||||||
$table-border-color: #ddd;
|
$table-border-color: transparent;
|
||||||
|
$table-cell-padding: 0.75rem 1rem;
|
||||||
|
|
|
@ -1,5 +1,54 @@
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
|
import { IOwnedDomainRequest, IBaseDomainRequest } from 'libs/ens';
|
||||||
|
import { REQUEST_STATES } from 'reducers/ens/domainRequests';
|
||||||
|
import { isCreationAddress } from 'libs/validators';
|
||||||
|
|
||||||
export function getEnsAddress(state: AppState, ensName: string): null | string {
|
export const getEns = (state: AppState) => state.ens;
|
||||||
return state.ens[ensName];
|
|
||||||
}
|
export const getCurrentDomainName = (state: AppState) => getEns(state).domainSelector.currentDomain;
|
||||||
|
|
||||||
|
export const getDomainRequests = (state: AppState) => getEns(state).domainRequests;
|
||||||
|
|
||||||
|
export const getCurrentDomainData = (state: AppState) => {
|
||||||
|
const currentDomain = getCurrentDomainName(state);
|
||||||
|
const domainRequests = getDomainRequests(state);
|
||||||
|
|
||||||
|
if (!currentDomain || !domainRequests[currentDomain] || domainRequests[currentDomain].error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const domainData = domainRequests[currentDomain].data || null;
|
||||||
|
|
||||||
|
return domainData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getResolvedAddress = (state: AppState, noGenesisAddress: boolean = false) => {
|
||||||
|
const data = getCurrentDomainData(state);
|
||||||
|
if (!data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOwned(data)) {
|
||||||
|
const { resolvedAddress } = data;
|
||||||
|
if (noGenesisAddress) {
|
||||||
|
return !isCreationAddress(resolvedAddress) ? resolvedAddress : null;
|
||||||
|
}
|
||||||
|
return data.resolvedAddress;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getResolvingDomain = (state: AppState) => {
|
||||||
|
const currentDomain = getCurrentDomainName(state);
|
||||||
|
const domainRequests = getDomainRequests(state);
|
||||||
|
|
||||||
|
if (!currentDomain || !domainRequests[currentDomain]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return domainRequests[currentDomain].state === REQUEST_STATES.pending;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isOwned = (data: IBaseDomainRequest): data is IOwnedDomainRequest => {
|
||||||
|
return !!(data as IOwnedDomainRequest).ownerAddress;
|
||||||
|
};
|
||||||
|
|
|
@ -13,7 +13,6 @@ interface ICurrentValue {
|
||||||
interface ICurrentTo {
|
interface ICurrentTo {
|
||||||
raw: string;
|
raw: string;
|
||||||
value: Address | null;
|
value: Address | null;
|
||||||
error?: string | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isEtherTransaction = (state: AppState) => {
|
const isEtherTransaction = (state: AppState) => {
|
||||||
|
|
|
@ -369,7 +369,7 @@ declare module 'bn.js' {
|
||||||
* @description reduct
|
* @description reduct
|
||||||
*/
|
*/
|
||||||
|
|
||||||
modn(b: number): BN;
|
modn(b: number): number; //API consistency https://github.com/indutny/bn.js/pull/130
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description rounded division
|
* @description rounded division
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import * as ens from 'libs/ens';
|
||||||
|
|
||||||
|
// TODO: write tests for:
|
||||||
|
// ens.placeBid
|
||||||
|
// ens.unsealBid
|
||||||
|
// ens.resolveDomainRequest
|
||||||
|
|
||||||
|
describe('ENS', () => {
|
||||||
|
it('converts a domain name to a normalized Unicode', () => {
|
||||||
|
const data = ens.normalise('xn--s-qfa0g.de');
|
||||||
|
expect(data).toBe('süß.de');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts a string to hexacedimal', () => {
|
||||||
|
const unicodeToHash = ens.getNameHash('Süß.de');
|
||||||
|
const asciiToHash = ens.getNameHash('xn--s-qfa0g.de');
|
||||||
|
expect(unicodeToHash && asciiToHash).toBe(
|
||||||
|
'0x26eb2a1d5e19a5d10e4a0001e7f3b22366f27d7203c6985b6b41fe65be107f8b'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -15,23 +15,15 @@ describe('customTokens reducer', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should handle CUSTOM_TOKEN_ADD', () => {
|
it('should handle CUSTOM_TOKEN_ADD', () => {
|
||||||
expect(
|
expect(customTokens(undefined, customTokensActions.addCustomToken(token1))).toEqual([token1]);
|
||||||
customTokens(undefined, customTokensActions.addCustomToken(token1))
|
|
||||||
).toEqual([token1]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle CUSTOM_TOKEN_REMOVE', () => {
|
it('should handle CUSTOM_TOKEN_REMOVE', () => {
|
||||||
const state1 = customTokens(
|
const state1 = customTokens(undefined, customTokensActions.addCustomToken(token1));
|
||||||
undefined,
|
const state2 = customTokens(state1, customTokensActions.addCustomToken(token2));
|
||||||
customTokensActions.addCustomToken(token1)
|
|
||||||
);
|
|
||||||
const state2 = customTokens(
|
|
||||||
state1,
|
|
||||||
customTokensActions.addCustomToken(token2)
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
expect(customTokens(state2, customTokensActions.removeCustomToken(token2.symbol))).toEqual([
|
||||||
customTokens(state2, customTokensActions.removeCustomToken(token2.symbol))
|
token1
|
||||||
).toEqual([token1]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import {
|
import { deterministicWallets, INITIAL_STATE } from 'reducers/deterministicWallets';
|
||||||
deterministicWallets,
|
|
||||||
INITIAL_STATE
|
|
||||||
} from 'reducers/deterministicWallets';
|
|
||||||
import * as dWalletActions from 'actions/deterministicWallets';
|
import * as dWalletActions from 'actions/deterministicWallets';
|
||||||
import { TokenValue } from 'libs/units';
|
import { TokenValue } from 'libs/units';
|
||||||
|
|
||||||
|
@ -23,10 +20,7 @@ describe('deterministicWallets reducer', () => {
|
||||||
it('should handle DW_SET_WALLETS', () => {
|
it('should handle DW_SET_WALLETS', () => {
|
||||||
const wallets = [wallet];
|
const wallets = [wallet];
|
||||||
expect(
|
expect(
|
||||||
deterministicWallets(
|
deterministicWallets(undefined, dWalletActions.setDeterministicWallets(wallets))
|
||||||
undefined,
|
|
||||||
dWalletActions.setDeterministicWallets(wallets)
|
|
||||||
)
|
|
||||||
).toEqual({
|
).toEqual({
|
||||||
...INITIAL_STATE,
|
...INITIAL_STATE,
|
||||||
wallets
|
wallets
|
||||||
|
@ -35,12 +29,7 @@ describe('deterministicWallets reducer', () => {
|
||||||
|
|
||||||
it('should handle DW_SET_DESIRED_TOKEN', () => {
|
it('should handle DW_SET_DESIRED_TOKEN', () => {
|
||||||
const desiredToken = 'OMG';
|
const desiredToken = 'OMG';
|
||||||
expect(
|
expect(deterministicWallets(undefined, dWalletActions.setDesiredToken(desiredToken))).toEqual({
|
||||||
deterministicWallets(
|
|
||||||
undefined,
|
|
||||||
dWalletActions.setDesiredToken(desiredToken)
|
|
||||||
)
|
|
||||||
).toEqual({
|
|
||||||
...INITIAL_STATE,
|
...INITIAL_STATE,
|
||||||
desiredToken
|
desiredToken
|
||||||
});
|
});
|
||||||
|
@ -56,10 +45,7 @@ describe('deterministicWallets reducer', () => {
|
||||||
address: 'wallet2'
|
address: 'wallet2'
|
||||||
};
|
};
|
||||||
const wallets = [wallet1, wallet2];
|
const wallets = [wallet1, wallet2];
|
||||||
const state = deterministicWallets(
|
const state = deterministicWallets(undefined, dWalletActions.setDeterministicWallets(wallets));
|
||||||
undefined,
|
|
||||||
dWalletActions.setDeterministicWallets(wallets)
|
|
||||||
);
|
|
||||||
|
|
||||||
const wallet2Update = {
|
const wallet2Update = {
|
||||||
...wallet,
|
...wallet,
|
||||||
|
@ -69,10 +55,7 @@ describe('deterministicWallets reducer', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
deterministicWallets(
|
deterministicWallets(state, dWalletActions.updateDeterministicWallet(wallet2Update))
|
||||||
state,
|
|
||||||
dWalletActions.updateDeterministicWallet(wallet2Update)
|
|
||||||
)
|
|
||||||
).toEqual({
|
).toEqual({
|
||||||
...INITIAL_STATE,
|
...INITIAL_STATE,
|
||||||
wallets: [wallet1, wallet2Update]
|
wallets: [wallet1, wallet2Update]
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { ens, INITIAL_STATE } from 'reducers/ens';
|
import { ens } from 'reducers/ens';
|
||||||
import * as ensActions from 'actions/ens';
|
import * as ensActions from 'actions/ens';
|
||||||
|
import { createStore } from 'redux';
|
||||||
|
const store = createStore(ens);
|
||||||
|
const INITIAL_STATE = store.getState();
|
||||||
|
|
||||||
describe('customTokens reducer', () => {
|
describe('customTokens reducer', () => {
|
||||||
it('should handle ENS_CACHE', () => {
|
it('handles resolveDomainRequested', () => {
|
||||||
const ensName = 'ensName';
|
const ensName = 'ensName';
|
||||||
const address = 'address';
|
expect(ens(undefined as any, ensActions.resolveDomainRequested(ensName))).toEqual({
|
||||||
expect(
|
|
||||||
ens(undefined, ensActions.cacheEnsAddress(ensName, address))
|
|
||||||
).toEqual({
|
|
||||||
...INITIAL_STATE,
|
...INITIAL_STATE,
|
||||||
[ensName]: address
|
domainRequests: { ensName: { state: 'PENDING' } },
|
||||||
|
domainSelector: { currentDomain: 'ensName' }
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,10 +19,7 @@ describe('customTokens reducer', () => {
|
||||||
const state2 = notifications(state1, notification2);
|
const state2 = notifications(state1, notification2);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
notifications(
|
notifications(state2, notificationsActions.closeNotification(notification2.payload))
|
||||||
state2,
|
|
||||||
notificationsActions.closeNotification(notification2.payload)
|
|
||||||
)
|
|
||||||
).toEqual([notification1.payload]);
|
).toEqual([notification1.payload]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,9 +15,7 @@ describe('rates reducer', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(rates(undefined, ratesActions.fetchCCRatesSucceeded(fakeCCResp))).toEqual({
|
||||||
rates(undefined, ratesActions.fetchCCRatesSucceeded(fakeCCResp))
|
|
||||||
).toEqual({
|
|
||||||
...INITIAL_STATE,
|
...INITIAL_STATE,
|
||||||
rates: {
|
rates: {
|
||||||
...INITIAL_STATE.rates,
|
...INITIAL_STATE.rates,
|
||||||
|
@ -27,8 +25,6 @@ describe('rates reducer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle RATES_FETCH_CC_FAILED', () => {
|
it('should handle RATES_FETCH_CC_FAILED', () => {
|
||||||
expect(rates(undefined, ratesActions.fetchCCRatesFailed())).toHaveProperty(
|
expect(rates(undefined, ratesActions.fetchCCRatesFailed())).toHaveProperty('ratesError');
|
||||||
'ratesError'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,26 +1,14 @@
|
||||||
import { delay } from 'redux-saga';
|
import { delay } from 'redux-saga';
|
||||||
import { call, put } from 'redux-saga/effects';
|
import { call, put } from 'redux-saga/effects';
|
||||||
import { handleNotification } from 'sagas/notifications';
|
import { handleNotification } from 'sagas/notifications';
|
||||||
import {
|
import { ShowNotificationAction, showNotification, closeNotification } from 'actions/notifications';
|
||||||
ShowNotificationAction,
|
|
||||||
showNotification,
|
|
||||||
closeNotification
|
|
||||||
} from 'actions/notifications';
|
|
||||||
|
|
||||||
describe('handleNotification*', () => {
|
describe('handleNotification*', () => {
|
||||||
const level = 'success';
|
const level = 'success';
|
||||||
const msg = 'msg';
|
const msg = 'msg';
|
||||||
const duration = 10;
|
const duration = 10;
|
||||||
const notificationAction1: ShowNotificationAction = showNotification(
|
const notificationAction1: ShowNotificationAction = showNotification(level, msg, duration);
|
||||||
level,
|
const notificationAction2: ShowNotificationAction = showNotification(level, msg, 0);
|
||||||
msg,
|
|
||||||
duration
|
|
||||||
);
|
|
||||||
const notificationAction2: ShowNotificationAction = showNotification(
|
|
||||||
level,
|
|
||||||
msg,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
const gen1 = handleNotification(notificationAction1);
|
const gen1 = handleNotification(notificationAction1);
|
||||||
const gen2 = handleNotification(notificationAction2);
|
const gen2 = handleNotification(notificationAction2);
|
||||||
|
|
||||||
|
|
|
@ -1,55 +1,57 @@
|
||||||
import { isEtherTransaction } from 'selectors/transaction';
|
|
||||||
import { setToField } from 'actions/transaction/actionCreators/fields';
|
|
||||||
import { setTokenTo } from 'actions/transaction/actionCreators/meta';
|
|
||||||
import { Address } from 'libs/units';
|
import { Address } from 'libs/units';
|
||||||
import { select, call, put } from 'redux-saga/effects';
|
import { call, select, put } from 'redux-saga/effects';
|
||||||
import { isValidETHAddress, isValidENSAddress } from 'libs/validators';
|
import { isValidETHAddress, isValidENSAddress } from 'libs/validators';
|
||||||
import { setCurrentTo } from 'sagas/transaction/current/currentTo';
|
import { setCurrentTo, setField } from 'sagas/transaction/current/currentTo';
|
||||||
|
import { isEtherTransaction } from 'selectors/transaction';
|
||||||
import { cloneableGenerator } from 'redux-saga/utils';
|
import { cloneableGenerator } from 'redux-saga/utils';
|
||||||
|
import { setToField, setTokenTo } from 'actions/transaction';
|
||||||
|
const raw = '0xa';
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
raw,
|
||||||
|
value: Address(raw)
|
||||||
|
};
|
||||||
|
|
||||||
describe('setCurrentTo*', () => {
|
describe('setCurrentTo*', () => {
|
||||||
const raw = '0xa';
|
|
||||||
const action: any = {
|
const action: any = {
|
||||||
payload: raw
|
payload: raw
|
||||||
};
|
};
|
||||||
const validAddress = true;
|
const validAddress = true;
|
||||||
const validEns = false;
|
const validEns = false;
|
||||||
const etherTransaction = true;
|
|
||||||
const payload = {
|
|
||||||
raw,
|
|
||||||
value: Address(raw),
|
|
||||||
error: null
|
|
||||||
};
|
|
||||||
|
|
||||||
const gens: any = {};
|
const gen = setCurrentTo(action);
|
||||||
gens.gen = cloneableGenerator(setCurrentTo)(action);
|
|
||||||
|
|
||||||
it('should call isValidETHAddress', () => {
|
it('should call isValidETHAddress', () => {
|
||||||
expect(gens.gen.next().value).toEqual(call(isValidETHAddress, raw));
|
expect(gen.next().value).toEqual(call(isValidETHAddress, raw));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call isValidENSAddress', () => {
|
it('should call isValidENSAddress', () => {
|
||||||
expect(gens.gen.next(validAddress).value).toEqual(call(isValidENSAddress, raw));
|
expect(gen.next(validAddress).value).toEqual(call(isValidENSAddress, raw));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select isEtherTransaction', () => {
|
it('should call setField', () => {
|
||||||
expect(gens.gen.next(validEns).value).toEqual(select(isEtherTransaction));
|
expect(gen.next(validEns).value).toEqual(call(setField, payload));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should put setToField if etherTransaction', () => {
|
it('should be done', () => {
|
||||||
gens.ethTransaction = gens.gen.clone();
|
expect(gen.next().done).toEqual(true);
|
||||||
expect(gens.ethTransaction.next(etherTransaction).value).toEqual(put(setToField(payload)));
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('setToField should be done', () => {
|
describe('setField', () => {
|
||||||
expect(gens.ethTransaction.next().done).toEqual(true);
|
const etherTransaction = cloneableGenerator(setField)(payload);
|
||||||
});
|
it('should select etherTransaction', () => {
|
||||||
|
expect(etherTransaction.next().value).toEqual(select(isEtherTransaction));
|
||||||
it('should put setTokenTo if !etherTransaction', () => {
|
});
|
||||||
expect(gens.gen.next(!etherTransaction).value).toEqual(put(setTokenTo(payload)));
|
|
||||||
});
|
it('should put setTokenTo field if its a token transaction ', () => {
|
||||||
|
const tokenTransaction = etherTransaction.clone();
|
||||||
it('setTokenTo should be done', () => {
|
|
||||||
expect(gens.gen.next().done).toEqual(true);
|
expect(tokenTransaction.next(false).value).toEqual(put(setTokenTo(payload)));
|
||||||
|
expect(tokenTransaction.next().done).toBe(true);
|
||||||
|
});
|
||||||
|
it('should put setToField if its an etherTransaction', () => {
|
||||||
|
expect(etherTransaction.next(true).value).toEqual(put(setToField(payload)));
|
||||||
|
expect(etherTransaction.next().done).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue