mirror of
https://github.com/trezor/trezor-wallet
synced 2025-07-03 21:32:34 +00:00
Add support for .crypto resolution
This commit is contained in:
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,
|
||||||
}
|
}
|
||||||
|
29
src/actions/constants/dotCrypto.js
Normal file
29
src/actions/constants/dotCrypto.js
Normal file
@ -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,6 +236,38 @@ export const onClear = (): AsyncAction => async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const onDomainChange = (domain: string): AsyncAction => async (
|
||||||
|
dispatch: Dispatch,
|
||||||
|
getState: GetState
|
||||||
|
): Promise<void> => {
|
||||||
|
const state = getState().sendFormEthereum;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
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 || domain,
|
||||||
|
resolvedDomain: address ? domain : '',
|
||||||
|
domainResolving: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called from UI on "address" field change
|
* Called from UI on "address" field change
|
||||||
*/
|
*/
|
||||||
@ -241,16 +276,25 @@ export const onAddressChange = (address: string): ThunkAction => (
|
|||||||
getState: GetState
|
getState: GetState
|
||||||
): void => {
|
): void => {
|
||||||
const state: State = getState().sendFormEthereum;
|
const state: State = getState().sendFormEthereum;
|
||||||
dispatch({
|
|
||||||
type: SEND.CHANGE,
|
if (state.domainResolving) {
|
||||||
networkType: 'ethereum',
|
return;
|
||||||
state: {
|
}
|
||||||
...state,
|
if (address.endsWith('.crypto')) {
|
||||||
untouched: false,
|
dispatch(onDomainChange(address));
|
||||||
touched: { ...state.touched, address: true },
|
} else {
|
||||||
address,
|
dispatch({
|
||||||
},
|
type: SEND.CHANGE,
|
||||||
});
|
networkType: 'ethereum',
|
||||||
|
state: {
|
||||||
|
...state,
|
||||||
|
untouched: false,
|
||||||
|
touched: { ...state.touched, address: true },
|
||||||
|
address,
|
||||||
|
domainResolving: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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…
Reference in New Issue
Block a user