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 = 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', networkType: 'ethereum',
state: EthereumState, 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 TX_ERROR: 'send__tx_error' = 'send__tx_error';
export const TOGGLE_ADVANCED: 'send__toggle_advanced' = 'send__toggle_advanced'; export const TOGGLE_ADVANCED: 'send__toggle_advanced' = 'send__toggle_advanced';
export const CLEAR: 'send__clear' = 'send__clear'; 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 AccountsActions from 'actions/AccountsActions';
import * as Web3Actions from 'actions/Web3Actions'; import * as Web3Actions from 'actions/Web3Actions';
import { mergeAccount, enhanceTransaction } from 'utils/accountUtils'; 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 { Dispatch, GetState, PromiseAction, Network } from 'flowtype';
import type { BlockchainNotification } from 'trezor-connect'; import type { BlockchainNotification } from 'trezor-connect';
import type { Token } from 'reducers/TokensReducer'; import type { Token } from 'reducers/TokensReducer';
@ -143,3 +144,18 @@ export const onError = (network: string): PromiseAction<void> => async (
): Promise<void> => { ): Promise<void> => {
dispatch(Web3Actions.disconnect(network)); 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', networkType: 'ethereum',
state: stateFromStorage, state: stateFromStorage,
}); });
if (stateFromStorage.domainResolving) {
dispatch(onDomainChange(stateFromStorage.address));
}
return; return;
} }
@ -233,26 +236,67 @@ export const onClear = (): AsyncAction => async (
}); });
}; };
/* export const onDomainChange = (domain: string): AsyncAction => async (
* Called from UI on "address" field change
*/
export const onAddressChange = (address: string): ThunkAction => (
dispatch: Dispatch, dispatch: Dispatch,
getState: GetState getState: GetState
): void => { ): Promise<void> => {
const state: State = getState().sendFormEthereum; const state = getState().sendFormEthereum;
dispatch({ 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', networkType: 'ethereum',
state: { state: {
...state, ...state,
untouched: false, untouched: false,
touched: { ...state.touched, address: true }, 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 * Called from UI on "amount" field change
*/ */

@ -81,8 +81,10 @@ export const validation = (): PayloadAction<State> => (
state.errors = {}; state.errors = {};
state.warnings = {}; state.warnings = {};
state.infos = {}; state.infos = {};
state = dispatch(recalculateTotalAmount(state)); state = dispatch(recalculateTotalAmount(state));
state = dispatch(updateCustomFeeLabel(state)); state = dispatch(updateCustomFeeLabel(state));
state = dispatch(domainResolution(state));
state = dispatch(addressValidation(state)); state = dispatch(addressValidation(state));
state = dispatch(addressLabel(state)); state = dispatch(addressLabel(state));
state = dispatch(amountValidation(state)); state = dispatch(amountValidation(state));
@ -164,6 +166,32 @@ export const addressValidation = ($state: State): PayloadAction<State> => (): St
return state; 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 * Address label assignation
*/ */

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

@ -3,6 +3,16 @@
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import EthereumjsUtil from 'ethereumjs-util'; 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 decimalToHex = (dec: number): string => new BigNumber(dec).toString(16);
export const padLeftEven = (hex: string): string => (hex.length % 2 !== 0 ? `0${hex}` : hex); export const padLeftEven = (hex: string): string => (hex.length % 2 !== 0 ? `0${hex}` : hex);

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

@ -31,6 +31,14 @@ const definedMessages: Messages = defineMessages({
'Looks like it is {deviceLabel} Account #{number} address of {network} network', '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', 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: { TR_IMPORTED_ACCOUNT_HASH: {
id: 'TR_IMPORTED_ACCOUNT_HASH', id: 'TR_IMPORTED_ACCOUNT_HASH',
defaultMessage: 'Imported account #{number}', defaultMessage: 'Imported account #{number}',

Loading…
Cancel
Save