import { Callback , CeloTx , Connection , JsonRpcPayload , JsonRpcResponse , Provider , } from '@celo/connect' ; import { privateKeyToAddress } from '@celo/utils/lib/address' ; import { recoverTransaction } from '@celo/wallet-base' ; import debugFactory from 'debug' ; import Web3 from 'web3' ; import { LocalWallet } from './local-wallet' ; const debug = debugFactory ( 'kit:txtest:sign' ) ; const PRIVATE_KEY1 = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' ; const ACCOUNT_ADDRESS1 = privateKeyToAddress ( PRIVATE_KEY1 ) ; const PRIVATE_KEY2 = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890fdeccc' ; const ACCOUNT_ADDRESS2 = privateKeyToAddress ( PRIVATE_KEY2 ) ; debug ( `Private key 1: ${ PRIVATE_KEY1 }` ) ; debug ( `Account Address 1: ${ ACCOUNT_ADDRESS1 }` ) ; debug ( `Private key 2: ${ PRIVATE_KEY2 }` ) ; debug ( `Account Address 2: ${ ACCOUNT_ADDRESS2 }` ) ; async function verifyLocalSigning ( web3 , celoTransaction ) { debug ( 'Signer Testing using Account: %s' , celoTransaction . from ) ; const signedTransaction = await web3 . eth . signTransaction ( celoTransaction ) ; debug ( 'Singer Testing: Signed transaction %o' , signedTransaction ) ; const rawTransaction = signedTransaction . raw ; const [ signedCeloTransaction , recoveredSigner ] = recoverTransaction ( rawTransaction ) ; debug ( 'Transaction was signed by "%s", recovered signer is "%s"' , celoTransaction . from , recoveredSigner ) ; expect ( recoveredSigner . toLowerCase ( ) ) . toEqual ( celoTransaction . from ! . toString ( ) . toLowerCase ( ) ) ; if ( celoTransaction . nonce != null ) { debug ( 'Checking nonce actual: %o expected: %o' , signedCeloTransaction . nonce , parseInt ( celoTransaction . nonce . toString ( ) , 16 ) ) ; expect ( signedCeloTransaction . nonce ) . toEqual ( parseInt ( celoTransaction . nonce . toString ( ) , 16 ) ) ; } if ( celoTransaction . gas != null ) { debug ( 'Checking gas actual %o expected %o' , signedCeloTransaction . gas , parseInt ( celoTransaction . gas . toString ( ) , 16 ) ) ; expect ( signedCeloTransaction . gas ) . toEqual ( parseInt ( celoTransaction . gas . toString ( ) , 16 ) ) ; } if ( celoTransaction . gasPrice != null ) { debug ( 'Checking gas price actual %o expected %o' , signedCeloTransaction . gasPrice , parseInt ( celoTransaction . gasPrice . toString ( ) , 16 ) ) ; expect ( signedCeloTransaction . gasPrice ) . toEqual ( parseInt ( celoTransaction . gasPrice . toString ( ) , 16 ) ) ; } if ( celoTransaction . feeCurrency != null ) { debug ( 'Checking fee currency actual %o expected %o' , signedCeloTransaction . feeCurrency , celoTransaction . feeCurrency ) ; expect ( signedCeloTransaction . feeCurrency ! . toLowerCase ( ) ) . toEqual ( celoTransaction . feeCurrency . toLowerCase ( ) ) ; } if ( celoTransaction . gatewayFeeRecipient != null ) { debug ( 'Checking gateway fee recipient actual ' + `${ signedCeloTransaction . gatewayFeeRecipient } expected ${ celoTransaction . gatewayFeeRecipient }` ) ; expect ( signedCeloTransaction . gatewayFeeRecipient ! . toLowerCase ( ) ) . toEqual ( celoTransaction . gatewayFeeRecipient . toLowerCase ( ) ) ; } if ( celoTransaction . gatewayFee != null ) { debug ( 'Checking gateway fee value actual %o expected %o' , signedCeloTransaction . gatewayFee , celoTransaction . gatewayFee . toString ( ) ) ; expect ( signedCeloTransaction . gatewayFee ) . toEqual ( celoTransaction . gatewayFee . toString ( ) ) ; } if ( celoTransaction . data != null ) { debug ( `Checking data actual ${ signedCeloTransaction . data } expected ${ celoTransaction . data }` ) ; expect ( signedCeloTransaction . data ! . toLowerCase ( ) ) . toEqual ( celoTransaction . data . toLowerCase ( ) ) ; } } async function verifyLocalSigningInAllPermutations ( web3 , from , to ) { const amountInWei = Web3 . utils . toWei ( '1' , 'ether' ) ; const nonce = 0 ; const badNonce = 100 ; const gas = 10 ; const gasPrice = 99 ; const feeCurrency = ACCOUNT_ADDRESS1 ; const gatewayFeeRecipient = ACCOUNT_ADDRESS2 ; const gatewayFee = '0x5678' ; const data = '0xabcdef' ; const chainId = 1 ; for ( let i = 0 ; i < 16 ; i ++ ) { const celoTransaction = { from , to , value : amountInWei , nonce , gasPrice , chainId , gas , feeCurrency : i & 1 ? feeCurrency : undefined , gatewayFeeRecipient : i & 2 ? gatewayFeeRecipient : undefined , gatewayFee : i & 4 ? gatewayFee : undefined , data : i & 8 ? data : undefined } ; await verifyLocalSigning ( web3 , celoTransaction ) ; } await verifyLocalSigning ( web3 , { from , to , nonce : badNonce , gas , gasPrice , chainId } ) ; } describe ( 'Transaction Utils' , ( ) => { let connection ; const mockProvider = { send : ( payload , callback ) : void => { if ( payload . method === 'eth_coinbase' ) { const response = { jsonrpc : payload . jsonrpc , id : Number ( payload . id ) , result : '0xc94770007dda54cF92009BFF0dE90c06F603a09f' } ; callback ( null , response ) ; } else { callback ( new Error ( payload . method ) ) ; } } } ; const web3 = new Web3 ( ) ; beforeEach ( ( ) => { web3 . setProvider ( mockProvider as any ) ; connection = new Connection ( web3 ) ; connection . wallet = new LocalWallet ( ) ; } ) ; afterEach ( ( ) => { connection . stop ( ) ; } ) ; describe ( 'Signer Testing with single local account and pay gas in CELO' , ( ) => { it ( 'Test1 should be able to sign and get the signer back with single local account' , async ( ) => { jest . setTimeout ( 60 * 1000 ) ; connection . addAccount ( PRIVATE_KEY1 ) ; await verifyLocalSigningInAllPermutations ( web3 , ACCOUNT_ADDRESS1 , ACCOUNT_ADDRESS2 ) ; } ) ; } ) ; describe ( 'Signer Testing with multiple local accounts' , ( ) => { it ( 'Test2 should be able to sign with first account and get the signer back with multiple local accounts' , async ( ) => { jest . setTimeout ( 60 * 1000 ) ; connection . addAccount ( PRIVATE_KEY1 ) ; connection . addAccount ( PRIVATE_KEY2 ) ; await verifyLocalSigningInAllPermutations ( web3 , ACCOUNT_ADDRESS1 , ACCOUNT_ADDRESS2 ) ; } ) ; it ( 'Test3 should be able to sign with second account and get the signer back with multiple local accounts' , async ( ) => { jest . setTimeout ( 60 * 1000 ) ; connection . addAccount ( PRIVATE_KEY1 ) ; connection . addAccount ( PRIVATE_KEY2 ) ; await verifyLocalSigningInAllPermutations ( web3 , ACCOUNT_ADDRESS2 , ACCOUNT_ADDRESS1 ) ; } ) ; } ) ; } ) ;