import { ensureLeading0x , normalizeAddressWith0x , trimLeading0x } from '@celo/base/lib/address' ; import { CeloTx , EncodedTransaction , RpcCaller , Signer } from '@celo/connect' ; import { EIP712TypedData } from '@celo/utils/lib/sign-typed-data-utils' ; import { decodeSig } from '@celo/wallet-base' ; import BigNumber from 'bignumber.js' ; import BN from 'bn.js' ; const INCORRECT_PASSWORD_ERROR = 'could not decrypt key with given password' ; const currentTimeInSeconds = ( ) => Math . floor ( Date . now ( ) / 1000 ) ; const toRpcHex = ( val ) => { if ( typeof val === 'number' || val instanceof BigNumber ) { return ensureLeading0x ( val . toString ( 16 ) ) ; } else if ( typeof val === 'string' ) { return ensureLeading0x ( val ) ; } else { return '0x0' ; } } ; enum RpcSignerEndpoint { ImportAccount = 'personal_importRawKey' , UnlockAccount = 'personal_unlockAccount' , SignTransaction = 'eth_signTransaction' , SignBytes = 'eth_sign' , SignTypedData = 'eth_signTypedData' , Decrypt = 'personal_decrypt' } type RpcSignerEndpointInputs = { personal_importRawKey : [ string , string ] ; personal_unlockAccount : [ string , string , number ] ; eth_signTransaction : [ any ] ; eth_sign : [ string , string ] ; eth_signTypedData : [ string , EIP712TypedData ] ; personal_decrypt : [ string , string ] ; } ; type RpcSignerEndpointResult = { personal_importRawKey : string ; personal_unlockAccount : boolean ; eth_signTransaction : EncodedTransaction ; eth_sign : string ; eth_signTypedData : string ; personal_decrypt : string ; } ; /**
 * Implements the signer interface on top of the JSON-RPC interface.
 */ export class RpcSigner implements Signer { param rpc param account param unlockBufferSeconds param unlockTime param unlockDuration constructor ( protected rpc , protected account , protected unlockBufferSeconds = 5 , protected unlockTime ? , protected unlockDuration ? ) { } init = ( privateKey , passphrase ) => this . callAndCheckResponse ( RpcSignerEndpoint . ImportAccount , [ trimLeading0x ( privateKey ) , passphrase , ] ) ; async signRawTransaction ( tx ) { if ( normalizeAddressWith0x ( tx . from ! as string ) !== this . account ) { throw new Error ( `RpcSigner cannot sign tx with 'from' ${ tx . from }` ) ; } const rpcTx = { ... tx , nonce : toRpcHex ( tx . nonce ) , value : toRpcHex ( tx . value ) , gas : toRpcHex ( tx . gas ) , gasPrice : toRpcHex ( tx . gasPrice ) , gatewayFee : toRpcHex ( tx . gatewayFee ) } ; return this . callAndCheckResponse ( RpcSignerEndpoint . SignTransaction , [ rpcTx ] ) ; } async signTransaction ( ) { throw new Error ( 'signTransaction unimplemented; use signRawTransaction' ) ; } async signTypedData ( typedData ) { const result = await this . callAndCheckResponse ( RpcSignerEndpoint . SignTypedData , [ this . account , typedData , ] ) ; return decodeSig ( result ) ; } async signPersonalMessage ( data ) { const result = await this . callAndCheckResponse ( RpcSignerEndpoint . SignBytes , [ this . account , data , ] ) ; return decodeSig ( result ) ; } getNativeKey = ( ) => this . account ; async unlock ( passphrase , duration ) { try { await this . callAndCheckResponse ( RpcSignerEndpoint . UnlockAccount , [ this . account , passphrase , duration , ] ) ; } catch ( error ) { if ( error ?. message ?. toLowerCase ( ) ?. includes ( INCORRECT_PASSWORD_ERROR ) ) { return false ; } throw error ; } this . unlockTime = currentTimeInSeconds ( ) ; this . unlockDuration = duration ; return true ; } isUnlocked ( ) { if ( this . unlockDuration === undefined || this . unlockTime === undefined ) { return false ; } return this . unlockTime + this . unlockDuration - this . unlockBufferSeconds > currentTimeInSeconds ( ) ; } private async callAndCheckResponse < T extends RpcSignerEndpoint > ( endpoint , params ) { const response = await this . rpc . call ( endpoint , params ) ; if ( response . error ) { throw new Error ( `RpcSigner@${ endpoint } failed with \n'${ ( response . error as any ) . message }'` ) ; } return response . result ! as RpcSignerEndpointResult [ typeof endpoint ] ; } async decrypt ( ciphertext ) { const resp = await this . callAndCheckResponse ( RpcSignerEndpoint . Decrypt , [ this . account , ensureLeading0x ( ciphertext . toString ( 'hex' ) ) , ] ) ; return Buffer . from ( trimLeading0x ( resp ) , 'hex' ) ; } computeSharedSecret ( _publicKey ) { throw new Error ( 'Not implemented' ) ; return Promise . resolve ( Buffer . from ( [ ] ) ) ; } }