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 const ENS_CACHE = 'ENS_CACHE';
|
||||
export enum TypeKeys {
|
||||
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: {
|
||||
raw: string;
|
||||
value: Address | null;
|
||||
error?: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ interface SetTokenToMetaAction {
|
|||
payload: {
|
||||
raw: string;
|
||||
value: Address | null;
|
||||
error?: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ interface Props {
|
|||
|
||||
export const AddressField: React.SFC<Props> = ({ isReadOnly }) => (
|
||||
<AddressFieldFactory
|
||||
withProps={({ currentTo, isValid, onChange, readOnly, errorMsg }) => (
|
||||
withProps={({ currentTo, isValid, onChange, readOnly }) => (
|
||||
<React.Fragment>
|
||||
<input
|
||||
className={`form-control ${isValid ? 'is-valid' : 'is-invalid'}`}
|
||||
|
@ -18,11 +18,6 @@ export const AddressField: React.SFC<Props> = ({ isReadOnly }) => (
|
|||
readOnly={!!(isReadOnly || readOnly)}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{errorMsg && (
|
||||
<div className="has-error">
|
||||
<span className="help-block">{errorMsg}</span>
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -18,14 +18,12 @@ export interface CallbackProps {
|
|||
isValid: boolean;
|
||||
readOnly: boolean;
|
||||
currentTo: ICurrentTo;
|
||||
errorMsg?: string | null;
|
||||
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() {
|
||||
// this 'to' parameter can be either token or actual field related
|
||||
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 {
|
||||
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
||||
}
|
||||
|
||||
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 };
|
||||
|
|
|
@ -1,29 +1,51 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Identicon } from 'components/ui';
|
||||
import { Identicon, Spinner } from 'components/ui';
|
||||
import translate from 'translations';
|
||||
//import { EnsAddress } from './components';
|
||||
import { Query } from 'components/renderCbs';
|
||||
import { ICurrentTo, getCurrentTo, isValidCurrentTo } from 'selectors/transaction';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { CallbackProps } from 'components/AddressFieldFactory';
|
||||
import { addHexPrefix } from 'ethereumjs-util';
|
||||
import { getResolvingDomain } from 'selectors/ens';
|
||||
import { isValidENSAddress } from 'libs/validators';
|
||||
|
||||
interface StateProps {
|
||||
currentTo: ICurrentTo;
|
||||
isValid: boolean;
|
||||
isResolving: boolean;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
onChange(ev: React.FormEvent<HTMLInputElement>): void;
|
||||
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;
|
||||
|
||||
//TODO: ENS handling
|
||||
class AddressInputFactoryClass extends Component<Props> {
|
||||
public render() {
|
||||
const { currentTo, onChange, isValid, withProps } = this.props;
|
||||
const { raw } = currentTo;
|
||||
const { currentTo, onChange, isValid, withProps, isResolving } = this.props;
|
||||
const { value } = currentTo;
|
||||
const addr = addHexPrefix(value ? value.toString('hex') : '0');
|
||||
return (
|
||||
<div className="row form-group">
|
||||
<div className="col-xs-11">
|
||||
|
@ -35,15 +57,14 @@ class AddressInputFactoryClass extends Component<Props> {
|
|||
currentTo,
|
||||
isValid,
|
||||
onChange,
|
||||
readOnly: !!readOnly,
|
||||
errorMsg: currentTo.error
|
||||
readOnly: !!readOnly || this.props.isResolving
|
||||
})
|
||||
}
|
||||
/>
|
||||
{/*<EnsAddress ensAddress={ensAddress} />*/}
|
||||
<ENSStatus ensAddress={currentTo.raw} isLoading={isResolving} rawAddress={addr} />
|
||||
</div>
|
||||
<div className="col-xs-1" style={{ padding: 0 }}>
|
||||
<Identicon address={/*ensAddress ||*/ raw} />
|
||||
<Identicon address={addr} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -52,5 +73,6 @@ class AddressInputFactoryClass extends Component<Props> {
|
|||
|
||||
export const AddressInputFactory = connect((state: AppState) => ({
|
||||
currentTo: getCurrentTo(state),
|
||||
isResolving: getResolvingDomain(state),
|
||||
isValid: isValidCurrentTo(state)
|
||||
}))(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;
|
||||
margin: $space-sm 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $grid-float-breakpoint) {
|
||||
.row {
|
||||
margin-left: -0.5rem;
|
||||
margin-right: -0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Modal {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { toTokenBase } from 'libs/units';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { getDecimal } from 'selectors/transaction';
|
||||
|
||||
interface IChildren {
|
||||
onUserInput: UnitConverter['onUserInput'];
|
||||
onUserInput: UnitConverterClass['onUserInput'];
|
||||
convertedUnit: string;
|
||||
}
|
||||
interface IFakeEvent {
|
||||
|
@ -24,7 +26,7 @@ interface State {
|
|||
|
||||
const initialState = { userInput: '' };
|
||||
|
||||
export class UnitConverter extends Component<Props, State> {
|
||||
class UnitConverterClass extends Component<Props, State> {
|
||||
public state: State = initialState;
|
||||
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
|
@ -58,3 +60,11 @@ export class UnitConverter extends Component<Props, State> {
|
|||
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) {
|
||||
const size = props.size || '4rem';
|
||||
// FIXME breaks on failed checksums
|
||||
const identiconDataUrl = isValidETHAddress(props.address.toLowerCase())
|
||||
? toDataUrl(props.address.toLowerCase())
|
||||
: '';
|
||||
const identiconDataUrl = isValidETHAddress(props.address) ? toDataUrl(props.address) : '';
|
||||
return (
|
||||
<div style={{ position: 'relative', width: size, height: size }} title="Address Identicon">
|
||||
<div
|
||||
|
|
|
@ -6,7 +6,6 @@ const ABIFUNC_METHOD_NAMES = ['encodeInput', 'decodeInput', 'decodeOutput'];
|
|||
enum ABIMethodTypes {
|
||||
FUNC = 'function'
|
||||
}
|
||||
|
||||
export type TContract = typeof 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 {
|
||||
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';
|
||||
|
||||
const INITIAL_STATE: State = {
|
||||
to: { raw: '', value: null, error: null },
|
||||
to: { raw: '', value: null },
|
||||
data: { raw: '', value: null },
|
||||
nonce: { raw: '', value: null },
|
||||
value: { raw: '', value: null },
|
||||
|
|
|
@ -18,7 +18,7 @@ const INITIAL_STATE: State = {
|
|||
previousUnit: 'ether',
|
||||
decimal: getDecimalFromEtherUnit('ether'),
|
||||
tokenValue: { raw: '', value: null },
|
||||
tokenTo: { raw: '', value: null, error: null },
|
||||
tokenTo: { raw: '', value: 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 { getBityRatesSaga, getShapeShiftRatesSaga, swapProviderSaga } from './swap/rates';
|
||||
import wallet from './wallet';
|
||||
import { ens } from './ens';
|
||||
import { transaction } from './transaction';
|
||||
|
||||
export default {
|
||||
ens,
|
||||
liteSend,
|
||||
configSaga,
|
||||
postBityOrderSaga,
|
||||
|
|
|
@ -3,26 +3,43 @@ import { SetCurrentToAction } from 'actions/transaction/actionTypes/current';
|
|||
import { setToField } from 'actions/transaction/actionCreators/fields';
|
||||
import { setTokenTo } from 'actions/transaction/actionCreators/meta';
|
||||
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 { isValidENSAddress, isValidETHAddress } from 'libs/validators';
|
||||
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 {
|
||||
const validAddress: boolean = yield call(isValidETHAddress, raw);
|
||||
const validEns: boolean = yield call(isValidENSAddress, raw);
|
||||
const etherTransaction: boolean = yield select(isEtherTransaction);
|
||||
|
||||
let value: Buffer | null = null;
|
||||
let error: string | null = null;
|
||||
if (validAddress) {
|
||||
value = Address(raw);
|
||||
} else if (validEns) {
|
||||
// TODO: Resolve ENS on networks that support it, error on ones that don't
|
||||
error = 'ENS is not supported yet';
|
||||
yield call(setField, { value, raw });
|
||||
|
||||
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) {
|
||||
yield put(setToField(payload));
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
$table-cell-padding: $space-sm;
|
||||
$table-condensed-cell-padding: $space-xs;
|
||||
$table-bg: transparent;
|
||||
$table-bg-accent: #f9f9f9;
|
||||
$table-bg-accent: #f5f5f5;
|
||||
$table-bg-hover: $gray-lightest;
|
||||
$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 { 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 {
|
||||
return state.ens[ensName];
|
||||
}
|
||||
export const getEns = (state: AppState) => state.ens;
|
||||
|
||||
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 {
|
||||
raw: string;
|
||||
value: Address | null;
|
||||
error?: string | null;
|
||||
}
|
||||
|
||||
const isEtherTransaction = (state: AppState) => {
|
||||
|
|
|
@ -369,7 +369,7 @@ declare module 'bn.js' {
|
|||
* @description reduct
|
||||
*/
|
||||
|
||||
modn(b: number): BN;
|
||||
modn(b: number): number; //API consistency https://github.com/indutny/bn.js/pull/130
|
||||
|
||||
/**
|
||||
* @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', () => {
|
||||
expect(
|
||||
customTokens(undefined, customTokensActions.addCustomToken(token1))
|
||||
).toEqual([token1]);
|
||||
expect(customTokens(undefined, customTokensActions.addCustomToken(token1))).toEqual([token1]);
|
||||
});
|
||||
|
||||
it('should handle CUSTOM_TOKEN_REMOVE', () => {
|
||||
const state1 = customTokens(
|
||||
undefined,
|
||||
customTokensActions.addCustomToken(token1)
|
||||
);
|
||||
const state2 = customTokens(
|
||||
state1,
|
||||
customTokensActions.addCustomToken(token2)
|
||||
);
|
||||
const state1 = customTokens(undefined, customTokensActions.addCustomToken(token1));
|
||||
const state2 = customTokens(state1, customTokensActions.addCustomToken(token2));
|
||||
|
||||
expect(
|
||||
customTokens(state2, customTokensActions.removeCustomToken(token2.symbol))
|
||||
).toEqual([token1]);
|
||||
expect(customTokens(state2, customTokensActions.removeCustomToken(token2.symbol))).toEqual([
|
||||
token1
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import {
|
||||
deterministicWallets,
|
||||
INITIAL_STATE
|
||||
} from 'reducers/deterministicWallets';
|
||||
import { deterministicWallets, INITIAL_STATE } from 'reducers/deterministicWallets';
|
||||
import * as dWalletActions from 'actions/deterministicWallets';
|
||||
import { TokenValue } from 'libs/units';
|
||||
|
||||
|
@ -23,10 +20,7 @@ describe('deterministicWallets reducer', () => {
|
|||
it('should handle DW_SET_WALLETS', () => {
|
||||
const wallets = [wallet];
|
||||
expect(
|
||||
deterministicWallets(
|
||||
undefined,
|
||||
dWalletActions.setDeterministicWallets(wallets)
|
||||
)
|
||||
deterministicWallets(undefined, dWalletActions.setDeterministicWallets(wallets))
|
||||
).toEqual({
|
||||
...INITIAL_STATE,
|
||||
wallets
|
||||
|
@ -35,12 +29,7 @@ describe('deterministicWallets reducer', () => {
|
|||
|
||||
it('should handle DW_SET_DESIRED_TOKEN', () => {
|
||||
const desiredToken = 'OMG';
|
||||
expect(
|
||||
deterministicWallets(
|
||||
undefined,
|
||||
dWalletActions.setDesiredToken(desiredToken)
|
||||
)
|
||||
).toEqual({
|
||||
expect(deterministicWallets(undefined, dWalletActions.setDesiredToken(desiredToken))).toEqual({
|
||||
...INITIAL_STATE,
|
||||
desiredToken
|
||||
});
|
||||
|
@ -56,10 +45,7 @@ describe('deterministicWallets reducer', () => {
|
|||
address: 'wallet2'
|
||||
};
|
||||
const wallets = [wallet1, wallet2];
|
||||
const state = deterministicWallets(
|
||||
undefined,
|
||||
dWalletActions.setDeterministicWallets(wallets)
|
||||
);
|
||||
const state = deterministicWallets(undefined, dWalletActions.setDeterministicWallets(wallets));
|
||||
|
||||
const wallet2Update = {
|
||||
...wallet,
|
||||
|
@ -69,10 +55,7 @@ describe('deterministicWallets reducer', () => {
|
|||
};
|
||||
|
||||
expect(
|
||||
deterministicWallets(
|
||||
state,
|
||||
dWalletActions.updateDeterministicWallet(wallet2Update)
|
||||
)
|
||||
deterministicWallets(state, dWalletActions.updateDeterministicWallet(wallet2Update))
|
||||
).toEqual({
|
||||
...INITIAL_STATE,
|
||||
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 { createStore } from 'redux';
|
||||
const store = createStore(ens);
|
||||
const INITIAL_STATE = store.getState();
|
||||
|
||||
describe('customTokens reducer', () => {
|
||||
it('should handle ENS_CACHE', () => {
|
||||
it('handles resolveDomainRequested', () => {
|
||||
const ensName = 'ensName';
|
||||
const address = 'address';
|
||||
expect(
|
||||
ens(undefined, ensActions.cacheEnsAddress(ensName, address))
|
||||
).toEqual({
|
||||
expect(ens(undefined as any, ensActions.resolveDomainRequested(ensName))).toEqual({
|
||||
...INITIAL_STATE,
|
||||
[ensName]: address
|
||||
domainRequests: { ensName: { state: 'PENDING' } },
|
||||
domainSelector: { currentDomain: 'ensName' }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,10 +19,7 @@ describe('customTokens reducer', () => {
|
|||
const state2 = notifications(state1, notification2);
|
||||
|
||||
expect(
|
||||
notifications(
|
||||
state2,
|
||||
notificationsActions.closeNotification(notification2.payload)
|
||||
)
|
||||
notifications(state2, notificationsActions.closeNotification(notification2.payload))
|
||||
).toEqual([notification1.payload]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,9 +15,7 @@ describe('rates reducer', () => {
|
|||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
rates(undefined, ratesActions.fetchCCRatesSucceeded(fakeCCResp))
|
||||
).toEqual({
|
||||
expect(rates(undefined, ratesActions.fetchCCRatesSucceeded(fakeCCResp))).toEqual({
|
||||
...INITIAL_STATE,
|
||||
rates: {
|
||||
...INITIAL_STATE.rates,
|
||||
|
@ -27,8 +25,6 @@ describe('rates reducer', () => {
|
|||
});
|
||||
|
||||
it('should handle RATES_FETCH_CC_FAILED', () => {
|
||||
expect(rates(undefined, ratesActions.fetchCCRatesFailed())).toHaveProperty(
|
||||
'ratesError'
|
||||
);
|
||||
expect(rates(undefined, ratesActions.fetchCCRatesFailed())).toHaveProperty('ratesError');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,26 +1,14 @@
|
|||
import { delay } from 'redux-saga';
|
||||
import { call, put } from 'redux-saga/effects';
|
||||
import { handleNotification } from 'sagas/notifications';
|
||||
import {
|
||||
ShowNotificationAction,
|
||||
showNotification,
|
||||
closeNotification
|
||||
} from 'actions/notifications';
|
||||
import { ShowNotificationAction, showNotification, closeNotification } from 'actions/notifications';
|
||||
|
||||
describe('handleNotification*', () => {
|
||||
const level = 'success';
|
||||
const msg = 'msg';
|
||||
const duration = 10;
|
||||
const notificationAction1: ShowNotificationAction = showNotification(
|
||||
level,
|
||||
msg,
|
||||
duration
|
||||
);
|
||||
const notificationAction2: ShowNotificationAction = showNotification(
|
||||
level,
|
||||
msg,
|
||||
0
|
||||
);
|
||||
const notificationAction1: ShowNotificationAction = showNotification(level, msg, duration);
|
||||
const notificationAction2: ShowNotificationAction = showNotification(level, msg, 0);
|
||||
const gen1 = handleNotification(notificationAction1);
|
||||
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 { select, call, put } from 'redux-saga/effects';
|
||||
import { call, select, put } from 'redux-saga/effects';
|
||||
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 { setToField, setTokenTo } from 'actions/transaction';
|
||||
const raw = '0xa';
|
||||
|
||||
const payload = {
|
||||
raw,
|
||||
value: Address(raw)
|
||||
};
|
||||
|
||||
describe('setCurrentTo*', () => {
|
||||
const raw = '0xa';
|
||||
const action: any = {
|
||||
payload: raw
|
||||
};
|
||||
const validAddress = true;
|
||||
const validEns = false;
|
||||
const etherTransaction = true;
|
||||
const payload = {
|
||||
raw,
|
||||
value: Address(raw),
|
||||
error: null
|
||||
};
|
||||
|
||||
const gens: any = {};
|
||||
gens.gen = cloneableGenerator(setCurrentTo)(action);
|
||||
const gen = setCurrentTo(action);
|
||||
|
||||
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', () => {
|
||||
expect(gens.gen.next(validAddress).value).toEqual(call(isValidENSAddress, raw));
|
||||
expect(gen.next(validAddress).value).toEqual(call(isValidENSAddress, raw));
|
||||
});
|
||||
|
||||
it('should select isEtherTransaction', () => {
|
||||
expect(gens.gen.next(validEns).value).toEqual(select(isEtherTransaction));
|
||||
it('should call setField', () => {
|
||||
expect(gen.next(validEns).value).toEqual(call(setField, payload));
|
||||
});
|
||||
|
||||
it('should put setToField if etherTransaction', () => {
|
||||
gens.ethTransaction = gens.gen.clone();
|
||||
expect(gens.ethTransaction.next(etherTransaction).value).toEqual(put(setToField(payload)));
|
||||
});
|
||||
|
||||
it('setToField should be done', () => {
|
||||
expect(gens.ethTransaction.next().done).toEqual(true);
|
||||
});
|
||||
|
||||
it('should put setTokenTo if !etherTransaction', () => {
|
||||
expect(gens.gen.next(!etherTransaction).value).toEqual(put(setTokenTo(payload)));
|
||||
});
|
||||
|
||||
it('setTokenTo should be done', () => {
|
||||
expect(gens.gen.next().done).toEqual(true);
|
||||
it('should be done', () => {
|
||||
expect(gen.next().done).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setField', () => {
|
||||
const etherTransaction = cloneableGenerator(setField)(payload);
|
||||
it('should select etherTransaction', () => {
|
||||
expect(etherTransaction.next().value).toEqual(select(isEtherTransaction));
|
||||
});
|
||||
|
||||
it('should put setTokenTo field if its a token transaction ', () => {
|
||||
const tokenTransaction = etherTransaction.clone();
|
||||
|
||||
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