Moved static Wallet calls to utils and refactors exposed types.
This commit is contained in:
parent
922de67a8b
commit
b61b84dfc8
@ -10,11 +10,17 @@ import { id } from '../utils/hash';
|
||||
import { keccak256 } from '../utils/keccak256';
|
||||
import { defineReadOnly, defineFrozen } from '../utils/properties';
|
||||
|
||||
import { BigNumber, BigNumberish, DeployDescription, EventDescription, EventFragment, FunctionDescription, FunctionFragment, Indexed, ParamType } from '../utils/types';
|
||||
import {
|
||||
BigNumber, BigNumberish,
|
||||
DeployDescription as _DeployDescription, EventDescription as _EventDescription, FunctionDescription as _FunctionDescription, LogDescription as _LogDescription, TransactionDescription as _TransactionDescription,
|
||||
EventFragment, FunctionFragment,
|
||||
Indexed as _Indexed,
|
||||
ParamType
|
||||
} from '../utils/types';
|
||||
|
||||
import * as errors from '../utils/errors';
|
||||
|
||||
class _Indexed extends Indexed {
|
||||
class Indexed extends _Indexed {
|
||||
constructor(hash: string) {
|
||||
super();
|
||||
defineReadOnly(this, 'hash', hash);
|
||||
@ -22,7 +28,6 @@ class _Indexed extends Indexed {
|
||||
}
|
||||
|
||||
class Description {
|
||||
readonly type: string;
|
||||
constructor(info: any) {
|
||||
for (var key in info) {
|
||||
let value = info[key];
|
||||
@ -35,8 +40,7 @@ class Description {
|
||||
}
|
||||
}
|
||||
|
||||
class _DeployDescription extends Description implements DeployDescription {
|
||||
readonly type: "deploy";
|
||||
class DeployDescription extends Description implements _DeployDescription {
|
||||
readonly inputs: Array<ParamType>;
|
||||
readonly payable: boolean;
|
||||
|
||||
@ -64,7 +68,7 @@ class _DeployDescription extends Description implements DeployDescription {
|
||||
}
|
||||
}
|
||||
|
||||
class _FunctionDescription extends Description implements FunctionDescription {
|
||||
class FunctionDescription extends Description implements _FunctionDescription {
|
||||
readonly type: "call" | "transaction";
|
||||
readonly name: string;
|
||||
readonly signature: string;
|
||||
@ -110,8 +114,7 @@ class Result extends Description {
|
||||
[key: number]: any;
|
||||
}
|
||||
|
||||
class _EventDescription extends Description implements EventDescription {
|
||||
readonly type: "event";
|
||||
class EventDescription extends Description implements _EventDescription {
|
||||
readonly name: string;
|
||||
readonly signature: string;
|
||||
|
||||
@ -198,10 +201,10 @@ class _EventDescription extends Description implements EventDescription {
|
||||
this.inputs.forEach(function(input, index) {
|
||||
if (input.indexed) {
|
||||
if (topics == null) {
|
||||
result[index] = new _Indexed(null);
|
||||
result[index] = new Indexed(null);
|
||||
|
||||
} else if (inputDynamic[index]) {
|
||||
result[index] = new _Indexed(resultIndexed[indexedIndex++]);
|
||||
result[index] = new Indexed(resultIndexed[indexedIndex++]);
|
||||
|
||||
} else {
|
||||
result[index] = resultIndexed[indexedIndex++];
|
||||
@ -218,7 +221,7 @@ class _EventDescription extends Description implements EventDescription {
|
||||
}
|
||||
}
|
||||
|
||||
class TransactionDescription extends Description {
|
||||
class TransactionDescription extends Description implements _TransactionDescription{
|
||||
readonly name: string;
|
||||
readonly args: Array<any>;
|
||||
readonly signature: string;
|
||||
@ -227,7 +230,7 @@ class TransactionDescription extends Description {
|
||||
readonly value: BigNumber;
|
||||
}
|
||||
|
||||
class LogDescription extends Description {
|
||||
class LogDescription extends Description implements _LogDescription {
|
||||
readonly name: string;
|
||||
readonly signature: string;
|
||||
readonly topic: string;
|
||||
@ -238,10 +241,9 @@ class LogDescription extends Description {
|
||||
function addMethod(method: any): void {
|
||||
switch (method.type) {
|
||||
case 'constructor': {
|
||||
let description = new _DeployDescription({
|
||||
let description = new DeployDescription({
|
||||
inputs: method.inputs,
|
||||
payable: (method.payable == null || !!method.payable),
|
||||
type: "deploy"
|
||||
payable: (method.payable == null || !!method.payable)
|
||||
});
|
||||
|
||||
if (!this.deployFunction) { this.deployFunction = description; }
|
||||
@ -253,7 +255,7 @@ function addMethod(method: any): void {
|
||||
let signature = formatSignature(method).replace(/tuple/g, '');
|
||||
let sighash = id(signature).substring(0, 10);
|
||||
|
||||
let description = new _FunctionDescription({
|
||||
let description = new FunctionDescription({
|
||||
inputs: method.inputs,
|
||||
outputs: method.outputs,
|
||||
|
||||
@ -280,15 +282,13 @@ function addMethod(method: any): void {
|
||||
case 'event': {
|
||||
let signature = formatSignature(method).replace(/tuple/g, '');
|
||||
|
||||
let description = new _EventDescription({
|
||||
let description = new EventDescription({
|
||||
name: method.name,
|
||||
signature: signature,
|
||||
|
||||
inputs: method.inputs,
|
||||
topic: id(signature),
|
||||
anonymous: (!!method.anonymous),
|
||||
|
||||
type: 'event'
|
||||
anonymous: (!!method.anonymous)
|
||||
});
|
||||
|
||||
// Expose the first (and hopefully unique) event name
|
||||
@ -304,7 +304,6 @@ function addMethod(method: any): void {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case 'fallback':
|
||||
// Nothing to do for fallback
|
||||
break;
|
||||
@ -317,9 +316,9 @@ function addMethod(method: any): void {
|
||||
|
||||
export class Interface {
|
||||
readonly abi: Array<EventFragment | FunctionFragment>;
|
||||
readonly functions: { [ name: string ]: FunctionDescription };
|
||||
readonly events: { [ name: string ]: EventDescription };
|
||||
readonly deployFunction: DeployDescription;
|
||||
readonly functions: { [ name: string ]: _FunctionDescription };
|
||||
readonly events: { [ name: string ]: _EventDescription };
|
||||
readonly deployFunction: _DeployDescription;
|
||||
|
||||
constructor(abi: Array<string | ParamType> | string) {
|
||||
errors.checkNew(this, Interface);
|
||||
@ -364,7 +363,7 @@ export class Interface {
|
||||
}
|
||||
}
|
||||
|
||||
parseTransaction(tx: { data: string, value?: BigNumberish }): TransactionDescription {
|
||||
parseTransaction(tx: { data: string, value?: BigNumberish }): _TransactionDescription {
|
||||
var sighash = tx.data.substring(0, 10).toLowerCase();
|
||||
for (var name in this.functions) {
|
||||
if (name.indexOf('(') === -1) { continue; }
|
||||
@ -377,7 +376,6 @@ export class Interface {
|
||||
name: name,
|
||||
signature: func.signature,
|
||||
sighash: func.sighash,
|
||||
type: 'transaction',
|
||||
value: bigNumberify(tx.value || 0),
|
||||
});
|
||||
}
|
||||
@ -386,7 +384,7 @@ export class Interface {
|
||||
return null;
|
||||
}
|
||||
|
||||
parseLog(log: { topics: Array<string>, data: string}): LogDescription {
|
||||
parseLog(log: { topics: Array<string>, data: string}): _LogDescription {
|
||||
for (var name in this.events) {
|
||||
if (name.indexOf('(') === -1) { continue; }
|
||||
var event = this.events[name];
|
||||
@ -399,7 +397,6 @@ export class Interface {
|
||||
name: event.name,
|
||||
signature: event.signature,
|
||||
topic: event.topic,
|
||||
type: 'log',
|
||||
values: event.decode(log.data, log.topics)
|
||||
});
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import { Web3Provider } from './web3-provider';
|
||||
|
||||
import { Network } from '../utils/types';
|
||||
|
||||
function getDefaultProvider(network?: Network | string): FallbackProvider {
|
||||
function getDefaultProvider(network?: Network | string): Provider {
|
||||
return new FallbackProvider([
|
||||
new InfuraProvider(network),
|
||||
new EtherscanProvider(network),
|
||||
|
@ -9,6 +9,7 @@ import * as base64 from './base64';
|
||||
import { bigNumberify, ConstantNegativeOne, ConstantZero, ConstantOne, ConstantTwo, ConstantWeiPerEther } from './bignumber';
|
||||
import { AddressZero, arrayify, concat, HashZero, hexDataSlice, hexDataLength, hexlify, hexStripZeros, hexZeroPad, joinSignature, padZeros, splitSignature, stripZeros } from './bytes';
|
||||
import { hashMessage, id, namehash } from './hash';
|
||||
import { getJsonWalletAddress } from './json-wallet';
|
||||
import { keccak256 } from './keccak256';
|
||||
import { sha256 } from './sha2';
|
||||
import { keccak256 as solidityKeccak256, pack as solidityPack, sha256 as soliditySha256 } from './solidity';
|
||||
@ -16,6 +17,7 @@ import { randomBytes } from './random-bytes';
|
||||
import { getNetwork } from './networks';
|
||||
import { defineFrozen, defineReadOnly, resolveProperties, shallowCopy } from './properties';
|
||||
import * as RLP from './rlp';
|
||||
import { verifyMessage } from './secp256k1';
|
||||
import { parse as parseTransaction, serialize as serializeTransaction } from './transaction';
|
||||
import { toUtf8Bytes, toUtf8String } from './utf8';
|
||||
import { formatEther, parseEther, formatUnits, parseUnits } from './units';
|
||||
@ -112,6 +114,10 @@ export {
|
||||
parseTransaction,
|
||||
serializeTransaction,
|
||||
|
||||
getJsonWalletAddress,
|
||||
|
||||
verifyMessage,
|
||||
|
||||
errors
|
||||
}
|
||||
|
||||
|
44
src.ts/utils/json-wallet.ts
Normal file
44
src.ts/utils/json-wallet.ts
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
import { getAddress } from './address';
|
||||
|
||||
export function isCrowdsaleWallet(json: string): boolean {
|
||||
try {
|
||||
var data = JSON.parse(json);
|
||||
} catch (error) { return false; }
|
||||
|
||||
return (data.encseed && data.ethaddr);
|
||||
}
|
||||
|
||||
export function isSecretStorageWallet(json: string): boolean {
|
||||
try {
|
||||
var data = JSON.parse(json);
|
||||
} catch (error) { return false; }
|
||||
|
||||
if (!data.version || parseInt(data.version) !== data.version || parseInt(data.version) !== 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @TODO: Put more checks to make sure it has kdf, iv and all that good stuff
|
||||
return true;
|
||||
}
|
||||
|
||||
//export function isJsonWallet(json: string): boolean {
|
||||
// return (isSecretStorageWallet(json) || isCrowdsaleWallet(json));
|
||||
//}
|
||||
|
||||
export function getJsonWalletAddress(json: string): string {
|
||||
if (isCrowdsaleWallet(json)) {
|
||||
try {
|
||||
return getAddress(JSON.parse(json).ethaddr);
|
||||
} catch (error) { return null; }
|
||||
}
|
||||
|
||||
if (isSecretStorageWallet(json)) {
|
||||
try {
|
||||
return getAddress(JSON.parse(json).address);
|
||||
} catch (error) { return null; }
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
import { ec as EC } from 'elliptic';
|
||||
const curve = new EC('secp256k1');
|
||||
|
||||
|
||||
import { getAddress } from './address';
|
||||
|
||||
import { arrayify, hexlify, hexZeroPad } from './bytes';
|
||||
import { arrayify, hexlify, hexZeroPad, splitSignature } from './bytes';
|
||||
import { hashMessage } from './hash';
|
||||
import { keccak256 } from './keccak256';
|
||||
import { defineReadOnly } from './properties';
|
||||
|
||||
@ -13,7 +13,6 @@ import { Arrayish, Signature } from './types';
|
||||
|
||||
import * as errors from './errors';
|
||||
|
||||
export const N = '0x' + curve.n.toString(16);
|
||||
|
||||
export class KeyPair {
|
||||
|
||||
@ -87,3 +86,28 @@ export function computeAddress(key: string): string {
|
||||
let publicKey = '0x' + computePublicKey(key).slice(4);
|
||||
return getAddress('0x' + keccak256(publicKey).substring(26));
|
||||
}
|
||||
|
||||
|
||||
export function verifyMessage(message: Arrayish | string, signature: Signature | string): string {
|
||||
let sig = splitSignature(signature);
|
||||
let digest = hashMessage(message);
|
||||
|
||||
return recoverAddress(
|
||||
digest,
|
||||
{
|
||||
r: sig.r,
|
||||
s: sig.s,
|
||||
recoveryParam: sig.recoveryParam
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// !!!!!! IMPORTANT !!!!!!!!
|
||||
//
|
||||
// This import MUST be at the bottom, otehrwise browserify executes several imports
|
||||
// BEFORE they are exported, resulting in undefined
|
||||
|
||||
import { ec as EC } from 'elliptic';
|
||||
const curve = new EC('secp256k1');
|
||||
|
||||
export const N = '0x' + curve.n.toString(16);
|
||||
|
@ -250,40 +250,55 @@ export interface TransactionResponse extends Transaction {
|
||||
///////////////////////////////
|
||||
// Interface
|
||||
|
||||
export class Indexed {
|
||||
hash: string;
|
||||
export abstract class Indexed {
|
||||
readonly hash: string;
|
||||
}
|
||||
|
||||
export interface DeployDescription {
|
||||
type: "deploy";
|
||||
inputs: Array<ParamType>;
|
||||
payable: boolean;
|
||||
readonly inputs: Array<ParamType>;
|
||||
readonly payable: boolean;
|
||||
encode(bytecode: string, params: Array<any>): string;
|
||||
}
|
||||
|
||||
export interface FunctionDescription {
|
||||
type: "call" | "transaction";
|
||||
name: string;
|
||||
signature: string;
|
||||
sighash: string;
|
||||
inputs: Array<ParamType>;
|
||||
outputs: Array<ParamType>;
|
||||
payable: boolean;
|
||||
readonly type: "call" | "transaction";
|
||||
readonly name: string;
|
||||
readonly signature: string;
|
||||
readonly sighash: string;
|
||||
readonly inputs: Array<ParamType>;
|
||||
readonly outputs: Array<ParamType>;
|
||||
readonly payable: boolean;
|
||||
encode(params: Array<any>): string;
|
||||
decode(data: string): any;
|
||||
}
|
||||
|
||||
export interface EventDescription {
|
||||
type: "event";
|
||||
name: string;
|
||||
signature: string;
|
||||
inputs: Array<ParamType>;
|
||||
anonymous: boolean;
|
||||
topic: string;
|
||||
readonly name: string;
|
||||
readonly signature: string;
|
||||
readonly inputs: Array<ParamType>;
|
||||
readonly anonymous: boolean;
|
||||
readonly topic: string;
|
||||
encodeTopics(params: Array<any>): Array<string>;
|
||||
decode(data: string, topics?: Array<string>): any;
|
||||
}
|
||||
|
||||
export interface LogDescription {
|
||||
readonly name: string;
|
||||
readonly signature: string;
|
||||
readonly topic: string;
|
||||
readonly values: Array<any>
|
||||
}
|
||||
|
||||
export interface TransactionDescription {
|
||||
readonly name: string;
|
||||
readonly args: Array<any>;
|
||||
readonly signature: string;
|
||||
readonly sighash: string;
|
||||
readonly decode: (data: string) => any;
|
||||
readonly value: BigNumber;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
// Contract
|
||||
|
||||
@ -322,7 +337,7 @@ export type Listener = (...args: Array<any>) => void;
|
||||
* Note: We use an abstract class so we can use instanceof to determine if an
|
||||
* object is a Provider.
|
||||
*/
|
||||
export abstract class MinimalProvider {
|
||||
export abstract class MinimalProvider implements OnceBlockable {
|
||||
abstract getNetwork(): Promise<Network>;
|
||||
|
||||
abstract getBlockNumber(): Promise<number>;
|
||||
@ -358,7 +373,7 @@ export abstract class MinimalProvider {
|
||||
}
|
||||
|
||||
export type AsyncProvider = {
|
||||
isMetaMask: boolean;
|
||||
isMetaMask?: boolean;
|
||||
host?: string;
|
||||
path?: string;
|
||||
sendAsync: (request: any, callback: (error: any, response: any) => void) => void
|
||||
|
@ -157,14 +157,14 @@ function _fromSeed(seed: Arrayish, mnemonic: string): HDNode {
|
||||
return new HDNode(I.slice(0, 32), I.slice(32), 0, 0, mnemonic, 'm');
|
||||
}
|
||||
|
||||
export function fromMnemonic(mnemonic: string, wordlist?: Wordlist): HDNode {
|
||||
export function fromMnemonic(mnemonic: string, wordlist?: Wordlist): _HDNode {
|
||||
// Check that the checksum s valid (will throw an error)
|
||||
mnemonicToEntropy(mnemonic, wordlist);
|
||||
|
||||
return _fromSeed(mnemonicToSeed(mnemonic), mnemonic);
|
||||
}
|
||||
|
||||
export function fromSeed(seed: Arrayish): HDNode {
|
||||
export function fromSeed(seed: Arrayish): _HDNode {
|
||||
return _fromSeed(seed, null);
|
||||
}
|
||||
|
||||
|
@ -64,26 +64,6 @@ function searchPath(object: any, path: string): string {
|
||||
return currentChild;
|
||||
}
|
||||
|
||||
export function isCrowdsaleWallet(json: string): boolean {
|
||||
try {
|
||||
var data = JSON.parse(json);
|
||||
} catch (error) { return false; }
|
||||
|
||||
return (data.encseed && data.ethaddr);
|
||||
}
|
||||
|
||||
export function isValidWallet(json: string): boolean {
|
||||
try {
|
||||
var data = JSON.parse(json);
|
||||
} catch (error) { return false; }
|
||||
|
||||
if (!data.version || parseInt(data.version) !== data.version || parseInt(data.version) !== 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @TODO: Put more checks to make sure it has kdf, iv and all that good stuff
|
||||
return true;
|
||||
}
|
||||
|
||||
// @TODO: Make a type for string or arrayish
|
||||
// See: https://github.com/ethereum/pyethsaletool
|
||||
|
@ -4,12 +4,12 @@ import { defaultPath, entropyToMnemonic, fromMnemonic } from './hdnode';
|
||||
import * as secretStorage from './secret-storage';
|
||||
import { SigningKey } from './signing-key';
|
||||
|
||||
import { arrayify, concat, hexlify, joinSignature } from '../utils/bytes';
|
||||
import { arrayify, concat, joinSignature } from '../utils/bytes';
|
||||
import { hashMessage } from '../utils/hash';
|
||||
import { isCrowdsaleWallet, isSecretStorageWallet } from '../utils/json-wallet';
|
||||
import { keccak256 } from '../utils/keccak256';
|
||||
import { defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
|
||||
import { randomBytes } from '../utils/random-bytes';
|
||||
import { recoverAddress } from '../utils/secp256k1';
|
||||
import { serialize as serializeTransaction } from '../utils/transaction';
|
||||
|
||||
import { Arrayish, BigNumber, BlockTag, HDNode, MinimalProvider, ProgressCallback, Signer, TransactionRequest, TransactionResponse, Wordlist } from '../utils/types';
|
||||
@ -160,7 +160,7 @@ export class Wallet extends Signer {
|
||||
}
|
||||
|
||||
static fromEncryptedJson(json: string, password: Arrayish, progressCallback: ProgressCallback): Promise<Wallet> {
|
||||
if (secretStorage.isCrowdsaleWallet(json)) {
|
||||
if (isCrowdsaleWallet(json)) {
|
||||
try {
|
||||
if (progressCallback) { progressCallback(0); }
|
||||
let privateKey = secretStorage.decryptCrowdsale(json, password);
|
||||
@ -170,7 +170,7 @@ export class Wallet extends Signer {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
} else if (secretStorage.isValidWallet(json)) {
|
||||
} else if (isSecretStorageWallet(json)) {
|
||||
|
||||
return secretStorage.decrypt(json, password, progressCallback).then(function(signingKey) {
|
||||
return new Wallet(signingKey);
|
||||
@ -184,35 +184,4 @@ export class Wallet extends Signer {
|
||||
if (!path) { path = defaultPath; }
|
||||
return new Wallet(fromMnemonic(mnemonic, wordlist).derivePath(path));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if this is an encryped JSON wallet.
|
||||
*/
|
||||
static isEncryptedWallet(json: string): boolean {
|
||||
return (secretStorage.isValidWallet(json) || secretStorage.isCrowdsaleWallet(json));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify a signed message, returning the address of the signer.
|
||||
*/
|
||||
static verifyMessage(message: Arrayish | string, signature: string): string {
|
||||
signature = hexlify(signature);
|
||||
if (signature.length != 132) { throw new Error('invalid signature'); }
|
||||
var digest = hashMessage(message);
|
||||
|
||||
var recoveryParam = parseInt(signature.substring(130), 16);
|
||||
if (recoveryParam >= 27) { recoveryParam -= 27; }
|
||||
if (recoveryParam < 0) { throw new Error('invalid signature'); }
|
||||
|
||||
return recoverAddress(
|
||||
digest,
|
||||
{
|
||||
r: signature.substring(0, 66),
|
||||
s: '0x' + signature.substring(66, 130),
|
||||
recoveryParam: recoveryParam
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user