2022-04-01 12:32:56 +00:00
import assert from 'assert' ;
2022-04-05 09:20:12 +00:00
import BIP32Factory from 'bip32' ;
import * as ecc from 'tiny-secp256k1' ;
import * as bip39 from 'bip39' ;
2022-04-01 12:32:56 +00:00
import { MessageTypes , signTypedData , SignTypedDataVersion } from '@metamask/eth-sig-util' ;
2022-04-05 09:20:12 +00:00
import { Ripemd160 , Secp256k1 } from "@cosmjs/crypto" ;
import { toBech32 , toHex } from '@cosmjs/encoding' ;
import { rawSecp256k1PubkeyToRawAddress } from "@cosmjs/amino" ;
const HDPATH = "m/44'/60'/0'/0" ;
const bip32 = BIP32Factory ( ecc ) ;
2022-04-01 12:32:56 +00:00
interface TypedMessageDomain {
name? : string ;
version? : string ;
chainId? : number ;
verifyingContract? : string ;
salt? : ArrayBuffer ;
}
/ * *
* Registry account .
* /
// TODO(egor): This is a wrapper around the private key and doesn't have any account related stuff (e.g. account number/sequence). Maybe rename to Key?
export class Account {
_privateKey : Buffer
_publicKey? : Uint8Array
2022-04-05 09:20:12 +00:00
_cosmosAddress? : string
_formattedCosmosAddress? : string
/ * *
* Generate bip39 mnemonic .
* /
static generateMnemonic() {
return bip39 . generateMnemonic ( ) ;
}
/ * *
* Generate private key from mnemonic .
* /
static async generateFromMnemonic ( mnemonic : string ) {
assert ( mnemonic ) ;
const seed = await bip39 . mnemonicToSeed ( mnemonic ) ;
const wallet = bip32 . fromSeed ( seed ) ;
const account = wallet . derivePath ( HDPATH ) ;
const { privateKey } = account ;
assert ( privateKey ) ;
return new Account ( privateKey ) ;
}
2022-04-01 12:32:56 +00:00
/ * *
* New Account .
* @param { buffer } privateKey
* /
constructor ( privateKey : Buffer ) {
assert ( privateKey ) ;
this . _privateKey = privateKey ;
}
get privateKey() {
return this . _privateKey ;
}
2022-04-05 09:20:12 +00:00
get formattedCosmosAddress() {
return this . _formattedCosmosAddress ;
}
2022-04-01 12:32:56 +00:00
async init ( ) {
// Generate public key.
const keypair = await Secp256k1 . makeKeypair ( this . _privateKey ) ;
const compressed = Secp256k1 . compressPubkey ( keypair . pubkey ) ;
this . _publicKey = compressed
2022-04-05 09:20:12 +00:00
// 2. Generate cosmos-sdk address.
// let publicKeySha256 = sha256(this._publicKey);
this . _cosmosAddress = new Ripemd160 ( ) . update ( keypair . pubkey ) . digest ( ) . toString ( ) ;
// 3. Generate cosmos-sdk formatted address.
this . _formattedCosmosAddress = toBech32 ( 'ethm' , rawSecp256k1PubkeyToRawAddress ( this . _publicKey ) ) ;
2022-04-01 12:32:56 +00:00
}
/ * *
* Get private key .
* /
getPrivateKey() {
return this . _privateKey . toString ( 'hex' ) ;
}
/ * *
* Sign message .
* /
sign ( message : any ) {
assert ( message ) ;
const eipMessageDomain : any = message . eipToSign . domain ;
const signature = signTypedData ( {
data : {
types : message.eipToSign.types as MessageTypes ,
primaryType : message.eipToSign.primaryType ,
domain : eipMessageDomain as TypedMessageDomain ,
message : message.eipToSign.message as Record < string , unknown >
} ,
privateKey : this._privateKey ,
version : SignTypedDataVersion.V4
} )
return signature ;
}
}