mirror of
https://github.com/status-im/MyCrypto.git
synced 2025-01-09 10:41:56 +00:00
Sign & Verify Message (#315)
* add route and nav tab for new module * add new module to tabs index * add signMessage to wallet interface * add signed message verification, normalize pkey sign * init Sign & Verify Message tab * reorder imports * mock out Trezor * cast to bool instead of length check * normalize ledger sign message * fix broken this context * add commented message signing to trezor wallet * correct var to start on sign tab * remove unused state var * clean up SignMessage classes * clean up VerifyMessage classes, remove unnecessary log * correct event variable types * remove unnecessary exports * remove empty classname * use implicit return * shorten signMessage method * remove unnecessary disable * tweak variable name * make better use of destructuring, remove console log * use destructured var * flatten if statement * add signMessage method to wallet reducer test
This commit is contained in:
parent
9d58329450
commit
68e5972a03
@ -9,6 +9,7 @@ import Help from 'containers/Tabs/Help';
|
|||||||
import SendTransaction from 'containers/Tabs/SendTransaction';
|
import SendTransaction from 'containers/Tabs/SendTransaction';
|
||||||
import Swap from 'containers/Tabs/Swap';
|
import Swap from 'containers/Tabs/Swap';
|
||||||
import ViewWallet from 'containers/Tabs/ViewWallet';
|
import ViewWallet from 'containers/Tabs/ViewWallet';
|
||||||
|
import SignAndVerifyMessage from 'containers/Tabs/SignAndVerifyMessage';
|
||||||
import BroadcastTx from 'containers/Tabs/BroadcastTx';
|
import BroadcastTx from 'containers/Tabs/BroadcastTx';
|
||||||
|
|
||||||
// TODO: fix this
|
// TODO: fix this
|
||||||
@ -32,6 +33,10 @@ export default class Root extends Component<Props, {}> {
|
|||||||
<Route path="/send-transaction" component={SendTransaction} />
|
<Route path="/send-transaction" component={SendTransaction} />
|
||||||
<Route path="/contracts" component={Contracts} />
|
<Route path="/contracts" component={Contracts} />
|
||||||
<Route path="/ens" component={ENS} />
|
<Route path="/ens" component={ENS} />
|
||||||
|
<Route
|
||||||
|
path="/sign-and-verify-message"
|
||||||
|
component={SignAndVerifyMessage}
|
||||||
|
/>
|
||||||
<Route path="/pushTx" component={BroadcastTx} />
|
<Route path="/pushTx" component={BroadcastTx} />
|
||||||
|
|
||||||
<LegacyRoutes />
|
<LegacyRoutes />
|
||||||
|
@ -28,6 +28,10 @@ const tabs = [
|
|||||||
name: 'NAV_ENS',
|
name: 'NAV_ENS',
|
||||||
to: 'ens'
|
to: 'ens'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Sign & Verify Message',
|
||||||
|
to: 'sign-and-verify-message'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Broadcast Transaction',
|
name: 'Broadcast Transaction',
|
||||||
to: 'pushTx'
|
to: 'pushTx'
|
||||||
|
@ -125,7 +125,5 @@ export default class TrezorDecrypt extends Component<Props, State> {
|
|||||||
this.props.onUnlock(new TrezorWallet(address, this.state.dPath, index));
|
this.props.onUnlock(new TrezorWallet(address, this.state.dPath, index));
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleNullConnect = (): void => {
|
private handleNullConnect = (): void => this.handleConnect();
|
||||||
return this.handleConnect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
.SignMessage {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 30px;
|
||||||
|
|
||||||
|
&-sign {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-help {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-inputBox {
|
||||||
|
min-height: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-error {
|
||||||
|
opacity: 0;
|
||||||
|
transition: none;
|
||||||
|
|
||||||
|
&.is-showing {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-buy {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { IWallet } from 'libs/wallet/IWallet';
|
||||||
|
import WalletDecrypt from 'components/WalletDecrypt';
|
||||||
|
import translate from 'translations';
|
||||||
|
import { showNotification, TShowNotification } from 'actions/notifications';
|
||||||
|
import { ISignedMessage } from 'libs/signing';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
wallet: IWallet;
|
||||||
|
showNotification: TShowNotification;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
message: string;
|
||||||
|
signMessageError: string;
|
||||||
|
signedMessage: ISignedMessage | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: State = {
|
||||||
|
message: '',
|
||||||
|
signMessageError: '',
|
||||||
|
signedMessage: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const messagePlaceholder =
|
||||||
|
'This is a sweet message that you are signing to prove that you own the address you say you own.';
|
||||||
|
|
||||||
|
export class SignMessage extends Component<Props, State> {
|
||||||
|
public state: State = initialState;
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { wallet } = this.props;
|
||||||
|
const { message, signedMessage } = this.state;
|
||||||
|
|
||||||
|
const messageBoxClass = classnames([
|
||||||
|
'SignMessage-inputBox',
|
||||||
|
'form-control',
|
||||||
|
message ? 'is-valid' : 'is-invalid'
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="Tab-content-pane">
|
||||||
|
<h4>{translate('MSG_message')}</h4>
|
||||||
|
<div className="form-group">
|
||||||
|
<textarea
|
||||||
|
className={messageBoxClass}
|
||||||
|
placeholder={messagePlaceholder}
|
||||||
|
value={message}
|
||||||
|
onChange={this.handleMessageChange}
|
||||||
|
/>
|
||||||
|
<div className="SignMessage-help">{translate('MSG_info2')}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!!wallet && (
|
||||||
|
<button
|
||||||
|
className="SignMessage-sign btn btn-primary btn-lg"
|
||||||
|
onClick={this.handleSignMessage}
|
||||||
|
>
|
||||||
|
{translate('NAV_SignMsg')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!!signedMessage && (
|
||||||
|
<div>
|
||||||
|
<h4>{translate('MSG_signature')}</h4>
|
||||||
|
<div className="form-group">
|
||||||
|
<textarea
|
||||||
|
className="SignMessage-inputBox form-control"
|
||||||
|
value={JSON.stringify(signedMessage, null, 2)}
|
||||||
|
disabled={true}
|
||||||
|
onChange={this.handleMessageChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!wallet && <WalletDecrypt />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleSignMessage = async () => {
|
||||||
|
const { wallet } = this.props;
|
||||||
|
const { message } = this.state;
|
||||||
|
|
||||||
|
if (!wallet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const signedMessage: ISignedMessage = {
|
||||||
|
address: await wallet.getAddress(),
|
||||||
|
message,
|
||||||
|
signature: await wallet.signMessage(message),
|
||||||
|
version: '2'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setState({ signedMessage });
|
||||||
|
this.props.showNotification(
|
||||||
|
'success',
|
||||||
|
`Successfully signed message with address ${signedMessage.address}.`
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
this.props.showNotification(
|
||||||
|
'danger',
|
||||||
|
`Error signing message: ${err.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleMessageChange = (e: React.FormEvent<HTMLTextAreaElement>) => {
|
||||||
|
const message = e.currentTarget.value;
|
||||||
|
this.setState({ message });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: AppState) {
|
||||||
|
return {
|
||||||
|
wallet: state.wallet.inst
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, {
|
||||||
|
showNotification
|
||||||
|
})(SignMessage);
|
@ -0,0 +1,28 @@
|
|||||||
|
.VerifyMessage {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 30px;
|
||||||
|
|
||||||
|
&-sign {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-help {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-inputBox {
|
||||||
|
min-height: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-success {
|
||||||
|
opacity: 1;
|
||||||
|
transition: none;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-buy {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import translate from 'translations';
|
||||||
|
import { showNotification, TShowNotification } from 'actions/notifications';
|
||||||
|
import { verifySignedMessage, ISignedMessage } from 'libs/signing';
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
showNotification: TShowNotification;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
signature: string;
|
||||||
|
verifiedAddress?: string;
|
||||||
|
verifiedMessage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: State = {
|
||||||
|
signature: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
const signaturePlaceholder =
|
||||||
|
'{"address":"0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8","message":"asdfasdfasdf","signature":"0x4771d78f13ba8abf608457f12471f427ca8f2fb046c1acb3f5969eefdfe452a10c9154136449f595a654b44b3b0163e86dd099beaca83bfd52d64c21da2221bb1c","version":"2"}';
|
||||||
|
|
||||||
|
export class VerifyMessage extends Component<Props, State> {
|
||||||
|
public state: State = initialState;
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { verifiedAddress, verifiedMessage, signature } = this.state;
|
||||||
|
|
||||||
|
const signatureBoxClass = classnames([
|
||||||
|
'VerifyMessage-inputBox',
|
||||||
|
'form-control',
|
||||||
|
signature ? 'is-valid' : 'is-invalid'
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="Tab-content-pane">
|
||||||
|
<h4>{translate('MSG_signature')}</h4>
|
||||||
|
<div className="form-group">
|
||||||
|
<textarea
|
||||||
|
className={signatureBoxClass}
|
||||||
|
placeholder={signaturePlaceholder}
|
||||||
|
value={signature}
|
||||||
|
onChange={this.handleSignatureChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="VerifyMessage-sign btn btn-primary btn-lg"
|
||||||
|
onClick={this.handleVerifySignedMessage}
|
||||||
|
disabled={false}
|
||||||
|
>
|
||||||
|
{translate('MSG_verify')}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{!!verifiedAddress &&
|
||||||
|
!!verifiedMessage && (
|
||||||
|
<div className="VerifyMessage-success alert alert-success">
|
||||||
|
<strong>{verifiedAddress}</strong> did sign the message{' '}
|
||||||
|
<strong>{verifiedMessage}</strong>.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearVerifiedData = () =>
|
||||||
|
this.setState({
|
||||||
|
verifiedAddress: '',
|
||||||
|
verifiedMessage: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
private handleVerifySignedMessage = () => {
|
||||||
|
try {
|
||||||
|
const parsedSignature: ISignedMessage = JSON.parse(this.state.signature);
|
||||||
|
|
||||||
|
if (!verifySignedMessage(parsedSignature)) {
|
||||||
|
throw Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { address, message } = parsedSignature;
|
||||||
|
this.setState({
|
||||||
|
verifiedAddress: address,
|
||||||
|
verifiedMessage: message
|
||||||
|
});
|
||||||
|
this.props.showNotification('success', translate('SUCCESS_7'));
|
||||||
|
} catch (err) {
|
||||||
|
this.clearVerifiedData();
|
||||||
|
this.props.showNotification('danger', translate('ERROR_12'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleSignatureChange = (e: React.FormEvent<HTMLTextAreaElement>) => {
|
||||||
|
const signature = e.currentTarget.value;
|
||||||
|
this.setState({ signature });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(null, {
|
||||||
|
showNotification
|
||||||
|
})(VerifyMessage);
|
30
common/containers/Tabs/SignAndVerifyMessage/index.scss
Normal file
30
common/containers/Tabs/SignAndVerifyMessage/index.scss
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
@import 'common/sass/variables';
|
||||||
|
@import 'common/sass/mixins';
|
||||||
|
|
||||||
|
.SignAndVerifyMsg {
|
||||||
|
&-header {
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&-tab {
|
||||||
|
@include reset-button;
|
||||||
|
color: $ether-blue;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
&,
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
color: $text-color;
|
||||||
|
cursor: default;
|
||||||
|
opacity: 1;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
common/containers/Tabs/SignAndVerifyMessage/index.tsx
Normal file
59
common/containers/Tabs/SignAndVerifyMessage/index.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import translate from 'translations';
|
||||||
|
import SignMessage from './components/SignMessage';
|
||||||
|
import VerifyMessage from './components/VerifyMessage';
|
||||||
|
import TabSection from 'containers/TabSection';
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
activeTab: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SignAndVerifyMessage extends Component<{}, State> {
|
||||||
|
public state: State = {
|
||||||
|
activeTab: 'sign'
|
||||||
|
};
|
||||||
|
|
||||||
|
public changeTab = activeTab => () => this.setState({ activeTab });
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { activeTab } = this.state;
|
||||||
|
let content;
|
||||||
|
let signActive = '';
|
||||||
|
let verifyActive = '';
|
||||||
|
|
||||||
|
if (activeTab === 'sign') {
|
||||||
|
content = <SignMessage />;
|
||||||
|
signActive = 'is-active';
|
||||||
|
} else {
|
||||||
|
content = <VerifyMessage />;
|
||||||
|
verifyActive = 'is-active';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TabSection>
|
||||||
|
<section className="Tab-content SignAndVerifyMsg">
|
||||||
|
<div className="Tab-content-pane">
|
||||||
|
<h1 className="SignAndVerifyMsg-header">
|
||||||
|
<button
|
||||||
|
className={`SignAndVerifyMsg-header-tab ${signActive}`}
|
||||||
|
onClick={this.changeTab('sign')}
|
||||||
|
>
|
||||||
|
{translate('Sign Message')}
|
||||||
|
</button>{' '}
|
||||||
|
<span>or</span>{' '}
|
||||||
|
<button
|
||||||
|
className={`SignAndVerifyMsg-header-tab ${verifyActive}`}
|
||||||
|
onClick={this.changeTab('verify')}
|
||||||
|
>
|
||||||
|
{translate('Verify Message')}
|
||||||
|
</button>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<main role="main">{content}</main>
|
||||||
|
</section>
|
||||||
|
</TabSection>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import { default as Help } from './Help';
|
|||||||
import { default as SendTransaction } from './SendTransaction';
|
import { default as SendTransaction } from './SendTransaction';
|
||||||
import { default as Swap } from './Swap';
|
import { default as Swap } from './Swap';
|
||||||
import { default as ViewWallet } from './ViewWallet';
|
import { default as ViewWallet } from './ViewWallet';
|
||||||
|
import { default as SignAndVerifyMessage } from './SignAndVerifyMessage';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
ENS,
|
ENS,
|
||||||
@ -11,5 +12,6 @@ export default {
|
|||||||
Help,
|
Help,
|
||||||
SendTransaction,
|
SendTransaction,
|
||||||
Swap,
|
Swap,
|
||||||
ViewWallet
|
ViewWallet,
|
||||||
|
SignAndVerifyMessage
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
import EthTx from 'ethereumjs-tx';
|
import EthTx from 'ethereumjs-tx';
|
||||||
import { ecsign, sha3 } from 'ethereumjs-util';
|
import {
|
||||||
|
addHexPrefix,
|
||||||
|
ecsign,
|
||||||
|
ecrecover,
|
||||||
|
sha3,
|
||||||
|
hashPersonalMessage,
|
||||||
|
toBuffer,
|
||||||
|
pubToAddress
|
||||||
|
} from 'ethereumjs-util';
|
||||||
import { RawTransaction } from 'libs/transaction';
|
import { RawTransaction } from 'libs/transaction';
|
||||||
import { isValidRawTx } from 'libs/validators';
|
import { isValidRawTx } from 'libs/validators';
|
||||||
|
import { stripHexPrefixAndLower } from 'libs/values';
|
||||||
|
|
||||||
export function signRawTxWithPrivKey(
|
export function signRawTxWithPrivKey(
|
||||||
privKey: Buffer,
|
privKey: Buffer,
|
||||||
@ -16,15 +25,10 @@ export function signRawTxWithPrivKey(
|
|||||||
return '0x' + eTx.serialize().toString('hex');
|
return '0x' + eTx.serialize().toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function signMessageWithPrivKey(
|
// adapted from:
|
||||||
privKey: Buffer,
|
// https://github.com/kvhnuke/etherwallet/blob/2a5bc0db1c65906b14d8c33ce9101788c70d3774/app/scripts/controllers/signMsgCtrl.js#L95
|
||||||
msg: string,
|
export function signMessageWithPrivKeyV2(privKey: Buffer, msg: string): string {
|
||||||
address: string,
|
const hash = hashPersonalMessage(toBuffer(msg));
|
||||||
date: string
|
|
||||||
): string {
|
|
||||||
const spacer = msg.length > 0 && date.length > 0 ? ' ' : '';
|
|
||||||
const fullMessage = msg + spacer + date;
|
|
||||||
const hash = sha3(fullMessage);
|
|
||||||
const signed = ecsign(hash, privKey);
|
const signed = ecsign(hash, privKey);
|
||||||
const combined = Buffer.concat([
|
const combined = Buffer.concat([
|
||||||
Buffer.from(signed.r),
|
Buffer.from(signed.r),
|
||||||
@ -33,9 +37,35 @@ export function signMessageWithPrivKey(
|
|||||||
]);
|
]);
|
||||||
const combinedHex = combined.toString('hex');
|
const combinedHex = combined.toString('hex');
|
||||||
|
|
||||||
return JSON.stringify({
|
return addHexPrefix(combinedHex);
|
||||||
address,
|
}
|
||||||
msg: fullMessage,
|
|
||||||
sig: '0x' + combinedHex
|
export interface ISignedMessage {
|
||||||
});
|
address: string;
|
||||||
|
message: string;
|
||||||
|
signature: string;
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapted from:
|
||||||
|
// https://github.com/kvhnuke/etherwallet/blob/2a5bc0db1c65906b14d8c33ce9101788c70d3774/app/scripts/controllers/signMsgCtrl.js#L118
|
||||||
|
export function verifySignedMessage({
|
||||||
|
address,
|
||||||
|
message,
|
||||||
|
signature,
|
||||||
|
version
|
||||||
|
}: ISignedMessage) {
|
||||||
|
const sig = new Buffer(stripHexPrefixAndLower(signature), 'hex');
|
||||||
|
if (sig.length !== 65) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//TODO: explain what's going on here
|
||||||
|
sig[64] = sig[64] === 0 || sig[64] === 1 ? sig[64] + 27 : sig[64];
|
||||||
|
const hash =
|
||||||
|
version === '2' ? hashPersonalMessage(toBuffer(message)) : sha3(message);
|
||||||
|
const pubKey = ecrecover(hash, sig[64], sig.slice(0, 32), sig.slice(32, 64));
|
||||||
|
|
||||||
|
return (
|
||||||
|
stripHexPrefixAndLower(address) === pubToAddress(pubKey).toString('hex')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,4 +3,5 @@ import { RawTransaction } from 'libs/transaction';
|
|||||||
export interface IWallet {
|
export interface IWallet {
|
||||||
getAddress(): Promise<string>;
|
getAddress(): Promise<string>;
|
||||||
signRawTransaction(tx: RawTransaction): Promise<string>;
|
signRawTransaction(tx: RawTransaction): Promise<string>;
|
||||||
|
signMessage(msg: string): Promise<string>;
|
||||||
}
|
}
|
||||||
|
@ -70,17 +70,8 @@ export default class LedgerWallet extends DeterministicWallet
|
|||||||
try {
|
try {
|
||||||
const combined = signed.r + signed.s + signed.v;
|
const combined = signed.r + signed.s + signed.v;
|
||||||
const combinedHex = combined.toString('hex');
|
const combinedHex = combined.toString('hex');
|
||||||
const signedMsg = JSON.stringify(
|
const signature = addHexPrefix(combinedHex);
|
||||||
{
|
resolve(signature);
|
||||||
address: await this.getAddress(),
|
|
||||||
msg,
|
|
||||||
sig: addHexPrefix(combinedHex),
|
|
||||||
version: '2'
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
);
|
|
||||||
resolve(signedMsg);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
toChecksumAddress
|
toChecksumAddress
|
||||||
} from 'ethereumjs-util';
|
} from 'ethereumjs-util';
|
||||||
import { pkeyToKeystore, UtcKeystore } from 'libs/keystore';
|
import { pkeyToKeystore, UtcKeystore } from 'libs/keystore';
|
||||||
import { signMessageWithPrivKey, signRawTxWithPrivKey } from 'libs/signing';
|
import { signMessageWithPrivKeyV2, signRawTxWithPrivKey } from 'libs/signing';
|
||||||
import { RawTransaction } from 'libs/transaction';
|
import { RawTransaction } from 'libs/transaction';
|
||||||
import { isValidPrivKey } from 'libs/validators';
|
import { isValidPrivKey } from 'libs/validators';
|
||||||
import { stripHexPrefixAndLower } from 'libs/values';
|
import { stripHexPrefixAndLower } from 'libs/values';
|
||||||
@ -69,13 +69,6 @@ export default class PrivKeyWallet implements IWallet {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public signMessage(msg: string, address: string, date: string): Promise<any> {
|
public signMessage = async (msg: string) =>
|
||||||
return new Promise((resolve, reject) => {
|
signMessageWithPrivKeyV2(this.privKey, msg);
|
||||||
try {
|
|
||||||
resolve(signMessageWithPrivKey(this.privKey, msg, address, date));
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -42,4 +42,27 @@ export default class TrezorWallet extends DeterministicWallet
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public signMessage = () =>
|
||||||
|
Promise.reject(new Error('Signing via Trezor not yet supported.'));
|
||||||
|
|
||||||
|
// works, but returns a signature that can only be verified with a Trezor device
|
||||||
|
/*
|
||||||
|
public signMessage = (message: string): Promise<string> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
(TrezorConnect as any).ethereumSignMessage(
|
||||||
|
this.getPath(),
|
||||||
|
message,
|
||||||
|
response => {
|
||||||
|
if (response.success) {
|
||||||
|
resolve(addHexPrefix(response.signature))
|
||||||
|
} else{
|
||||||
|
console.error(response.error)
|
||||||
|
reject(response.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,8 @@ describe('wallet reducer', () => {
|
|||||||
|
|
||||||
const walletInstance = {
|
const walletInstance = {
|
||||||
getAddress: () => doSomething,
|
getAddress: () => doSomething,
|
||||||
signRawTransaction: () => doSomething
|
signRawTransaction: () => doSomething,
|
||||||
|
signMessage: () => doSomething
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(wallet(undefined, walletActions.setWallet(walletInstance))).toEqual({
|
expect(wallet(undefined, walletActions.setWallet(walletInstance))).toEqual({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user