Add support for .crypto resolution

pull/638/head
sudoryan 4 years ago
parent 8e9d704e05
commit 64bc952864

@ -13,7 +13,13 @@ import * as RippleSendFormActions from './ripple/SendFormActions';
export type SendFormAction =
| {
type: typeof SEND.INIT | typeof SEND.VALIDATION | typeof SEND.CHANGE | typeof SEND.CLEAR,
type:
| typeof SEND.INIT
| typeof SEND.VALIDATION
| typeof SEND.CHANGE
| typeof SEND.CLEAR
| typeof SEND.DOMAIN_RESOLVING
| typeof SEND.DOMAIN_COMPLETE,
networkType: 'ethereum',
state: EthereumState,
}

@ -0,0 +1,29 @@
export const ProxyReader = {
address: '0x7ea9Ee21077F84339eDa9C80048ec6db678642B1',
abi: [
{
constant: true,
inputs: [
{ internalType: 'string', name: 'key', type: 'string' },
{ internalType: 'uint256', name: 'tokenId', type: 'uint256' },
],
name: 'get',
outputs: [{ internalType: 'string', name: '', type: 'string' }],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: true,
inputs: [
{ internalType: 'string[]', name: 'keys', type: 'string[]' },
{ internalType: 'uint256', name: 'tokenId', type: 'uint256' },
],
name: 'getMany',
outputs: [{ internalType: 'string[]', name: '', type: 'string[]' }],
payable: false,
stateMutability: 'view',
type: 'function',
},
],
};

@ -8,3 +8,5 @@ export const TX_COMPLETE: 'send__tx_complete' = 'send__tx_complete';
export const TX_ERROR: 'send__tx_error' = 'send__tx_error';
export const TOGGLE_ADVANCED: 'send__toggle_advanced' = 'send__toggle_advanced';
export const CLEAR: 'send__clear' = 'send__clear';
export const DOMAIN_RESOLVING: 'send__domain_resolving' = 'send__domain_resolving';
export const DOMAIN_COMPLETE: 'send__domain_complete' = 'send__domain_complete';

@ -6,7 +6,8 @@ import * as PENDING from 'actions/constants/pendingTx';
import * as AccountsActions from 'actions/AccountsActions';
import * as Web3Actions from 'actions/Web3Actions';
import { mergeAccount, enhanceTransaction } from 'utils/accountUtils';
import { namehash } from 'utils/ethUtils';
import * as dotCrypto from 'actions/constants/dotCrypto';
import type { Dispatch, GetState, PromiseAction, Network } from 'flowtype';
import type { BlockchainNotification } from 'trezor-connect';
import type { Token } from 'reducers/TokensReducer';
@ -143,3 +144,18 @@ export const onError = (network: string): PromiseAction<void> => async (
): Promise<void> => {
dispatch(Web3Actions.disconnect(network));
};
export const resolveDomain = (domain: string, ticker: string): PromiseAction<void> => async (
dispatch: Dispatch
): Promise<string> => {
const instance = await dispatch(Web3Actions.initWeb3('eth'));
const ProxyReader = new instance.web3.eth.Contract(
dotCrypto.ProxyReader.abi,
dotCrypto.ProxyReader.address
);
const node = namehash(domain);
return ProxyReader.methods
.get(`crypto.${ticker}.address`, node)
.call()
.catch(() => '');
};

@ -144,6 +144,9 @@ export const init = (): AsyncAction => async (
networkType: 'ethereum',
state: stateFromStorage,
});
if (stateFromStorage.domainResolving) {
dispatch(onDomainChange(stateFromStorage.address));
}
return;
}
@ -233,26 +236,67 @@ export const onClear = (): AsyncAction => async (
});
};
/*
* Called from UI on "address" field change
*/
export const onAddressChange = (address: string): ThunkAction => (
export const onDomainChange = (domain: string): AsyncAction => async (
dispatch: Dispatch,
getState: GetState
): void => {
const state: State = getState().sendFormEthereum;
): Promise<void> => {
const state = getState().sendFormEthereum;
dispatch({
type: SEND.CHANGE,
type: SEND.DOMAIN_RESOLVING,
networkType: 'ethereum',
state: {
...state,
address: domain,
resolvedDomain: '',
domainResolving: true,
},
});
const address = await dispatch(BlockchainActions.resolveDomain(domain, 'ETH'));
dispatch({
type: SEND.DOMAIN_COMPLETE,
networkType: 'ethereum',
state: {
...state,
untouched: false,
touched: { ...state.touched, address: true },
address,
address: address || domain,
resolvedDomain: address ? domain : '',
domainResolving: false,
},
});
};
/*
* Called from UI on "address" field change
*/
export const onAddressChange = (address: string): ThunkAction => (
dispatch: Dispatch,
getState: GetState
): void => {
const state: State = getState().sendFormEthereum;
if (state.domainResolving) {
return;
}
if (address.endsWith('.crypto')) {
dispatch(onDomainChange(address));
} else {
dispatch({
type: SEND.CHANGE,
networkType: 'ethereum',
state: {
...state,
untouched: false,
touched: { ...state.touched, address: true },
address,
domainResolving: false,
},
});
}
};
/*
* Called from UI on "amount" field change
*/

@ -81,8 +81,10 @@ export const validation = (): PayloadAction<State> => (
state.errors = {};
state.warnings = {};
state.infos = {};
state = dispatch(recalculateTotalAmount(state));
state = dispatch(updateCustomFeeLabel(state));
state = dispatch(domainResolution(state));
state = dispatch(addressValidation(state));
state = dispatch(addressLabel(state));
state = dispatch(amountValidation(state));
@ -164,6 +166,32 @@ export const addressValidation = ($state: State): PayloadAction<State> => (): St
return state;
};
/*
* Domain resolving
*/
export const domainResolution = ($state: State): PayloadAction<State> => (): State => {
const state = { ...$state };
if (state.domainResolving) {
state.warnings.address = {
...l10nCommonMessages.TR_DOMAIN_RESOLVING,
values: {
domain: state.address,
},
};
}
if (state.resolvedDomain) {
state.infos.address = {
...l10nCommonMessages.TR_DOMAIN_COMPLETE,
values: {
domain: state.resolvedDomain,
address: state.address,
},
};
}
return state;
};
/*
* Address label assignation
*/

@ -42,6 +42,8 @@ export type State = {
infos: { [k: string]: MessageDescriptor },
sending: boolean,
domainResolving: boolean,
resolvedDomain: string,
};
export const initialState: State = {
@ -74,6 +76,8 @@ export const initialState: State = {
nonce: '0',
total: '0',
sending: false,
domainResolving: false,
resolvedDomain: '',
errors: {},
warnings: {},
infos: {},
@ -87,6 +91,8 @@ export default (state: State = initialState, action: Action): State => {
case SEND.INIT:
case SEND.CHANGE:
case SEND.VALIDATION:
case SEND.DOMAIN_RESOLVING:
case SEND.DOMAIN_COMPLETE:
case SEND.CLEAR:
return action.state;

@ -3,6 +3,16 @@
import BigNumber from 'bignumber.js';
import EthereumjsUtil from 'ethereumjs-util';
export const namehash = (domain: string): string => {
if (!domain) {
return '0x0000000000000000000000000000000000000000000000000000000000000000';
}
const [label, ...remainder] = domain.split('.');
return `0x${EthereumjsUtil.keccak256(
namehash(remainder.join('.')) + EthereumjsUtil.keccak256(label).toString('hex')
).toString('hex')}`;
};
export const decimalToHex = (dec: number): string => new BigNumber(dec).toString(16);
export const padLeftEven = (hex: string): string => (hex.length % 2 !== 0 ? `0${hex}` : hex);

@ -206,13 +206,14 @@ const StyledIcon = styled(Icon)`
const getAddressInputState = (
address: string,
addressErrors: boolean,
addressWarnings: boolean
addressWarnings: boolean,
domainResolving: boolean
): ?string => {
let state = null;
if (address && !addressErrors) {
state = 'success';
}
if (addressWarnings && !addressErrors) {
if ((addressWarnings && !addressErrors) || domainResolving) {
state = 'warning';
}
if (addressErrors) {
@ -270,6 +271,7 @@ const AccountSend = (props: Props) => {
infos,
sending,
advanced,
domainResolving,
} = props.sendForm;
const {
@ -314,6 +316,7 @@ const AccountSend = (props: Props) => {
amount.length === 0 ||
address.length === 0 ||
sending ||
domainResolving ||
account.imported;
let amountText = '';
@ -348,7 +351,12 @@ const AccountSend = (props: Props) => {
</Title>
<InputRow>
<Input
state={getAddressInputState(address, !!errors.address, !!warnings.address)}
state={getAddressInputState(
address,
!!errors.address,
!!warnings.address,
domainResolving
)}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"

@ -31,6 +31,14 @@ const definedMessages: Messages = defineMessages({
'Looks like it is {deviceLabel} Account #{number} address of {network} network',
description: 'Example: Looks like it is My Trezor Account #1 address of ETH network',
},
TR_DOMAIN_RESOLVING: {
id: 'TR_DOMAIN_RESOLVING',
defaultMessage: 'Resolving {domain}...',
},
TR_DOMAIN_COMPLETE: {
id: 'TR_DOMAIN_COMPLETE',
defaultMessage: '{domain} resolved to: {address}',
},
TR_IMPORTED_ACCOUNT_HASH: {
id: 'TR_IMPORTED_ACCOUNT_HASH',
defaultMessage: 'Imported account #{number}',

Loading…
Cancel
Save