import { Transaction , util , Wallet } from '@zilliqa-js/account' ; import { fromBech32Address } from '@zilliqa-js/crypto' ; import { validation } from '@zilliqa-js/util' ; import { BlockchainInfo , BlockList , DsBlockObj , GET_TX_ATTEMPTS , TransactionStatusObj , Provider , RPCMethod , RPCResponse , ShardingStructure , sign , TransactionObj , MinerInfo , TxBlockObj , TxList , ZilliqaModule , } from '@zilliqa-js/core' ; import { toTxParams } from './util' ; const isBlockNumber = ( blockNum ) => Number . isFinite ( blockNum ) && Number . isInteger ( blockNum ) && blockNum >= 0 ; export class Blockchain implements ZilliqaModule { signer : Wallet ; provider : Provider ; pendingErrorMap : { [ key ] : string ; } = { 0 : 'Transaction not found' , 1 : 'Pending - Dispatched' , 2 : 'Pending - Soft-confirmed (awaiting Tx block generation)' , 4 : 'Pending - Nonce is higher than expected' , 5 : 'Pending - Microblock gas limit exceeded' , 6 : 'Pending - Consensus failure in network' , 3 : 'Confirmed' , 10 : 'Rejected - Transaction caused math error' , 11 : 'Rejected - Scilla invocation error' , 12 : 'Rejected - Contract account initialization error' , 13 : 'Rejected - Invalid source account' , 14 : 'Rejected - Gas limit higher than shard gas limit' , 15 : 'Rejected - Unknown transaction type' , 16 : 'Rejected - Transaction sent to wrong shard' , 17 : 'Rejected - Contract & source account cross-shard issue' , 18 : 'Rejected - Code size exceeded limit' , 19 : 'Rejected - Transaction verification failed' , 20 : 'Rejected - Gas limit too low' , 21 : 'Rejected - Insufficient balance' , 22 : 'Rejected - Insufficient gas to invoke Scilla checker' , 23 : 'Rejected - Duplicate transaction exists' , 24 : 'Rejected - Transaction with same nonce but same/higher gas price exists' , 25 : 'Rejected - Invalid destination address' , 26 : 'Rejected - Failed to add contract account to state' , 27 : 'Rejected - Nonce is lower than expected' , 255 : 'Rejected - Internal error' , } ; transactionStatusMap : { [ key ] : { [ key ] : string ; } ; } = { 0 : { 0 : 'Transaction not found' , 1 : ' Pending - Dispatched' } , 1 : { 2 : 'Pending - Soft-confirmed (awaiting Tx block generation)' , 4 : 'Pending - Nonce is higher than expected' , 5 : 'Pending - Microblock gas limit exceeded' , 6 : 'Pending - Consensus failure in network' , } , 2 : { 3 : 'Confirmed' , 10 : 'Rejected - Transaction caused math error' , 11 : 'Rejected - Scilla invocation error' , 12 : 'Rejected - Contract account initialization error' , 13 : 'Rejected - Invalid source account' , 14 : 'Rejected - Gas limit higher than shard gas limit' , 15 : 'Rejected - Unknown transaction type' , 16 : 'Rejected - Transaction sent to wrong shard' , 17 : 'Rejected - Contract & source account cross-shard issue' , 18 : 'Rejected - Code size exceeded limit' , 19 : 'Rejected - Transaction verification failed' , 20 : 'Rejected - Gas limit too low' , 21 : 'Rejected - Insufficient balance' , 22 : 'Rejected - Insufficient gas to invoke Scilla checker' , 23 : 'Rejected - Duplicate transaction exists' , 24 : 'Rejected - Transaction with higher gas price exists' , 25 : 'Rejected - Invalid destination address' , 26 : 'Rejected - Failed to add contract account to state' , 27 : 'Rejected - Nonce is lower than expected' , 255 : 'Rejected - Internal error' , } , } ; constructor ( provider , signer ) { this . provider = provider ; this . provider . middleware . request . use ( util . formatOutgoingTx , RPCMethod . CreateTransaction ) ; this . signer = signer ; } getBlockChainInfo ( ) { return this . provider . send ( RPCMethod . GetBlockchainInfo ) ; } getShardingStructure ( ) { return this . provider . send ( RPCMethod . GetShardingStructure ) ; } getDSBlock ( blockNum ) { return this . provider . send ( RPCMethod . GetDSBlock , blockNum . toString ( ) ) ; } getLatestDSBlock ( ) { return this . provider . send ( RPCMethod . GetLatestDSBlock ) ; } getNumDSBlocks ( ) { return this . provider . send ( RPCMethod . GetNumDSBlocks ) ; } getDSBlockRate ( ) { return this . provider . send ( RPCMethod . GetDSBlockRate ) ; } getDSBlockListing ( max ) { return this . provider . send ( RPCMethod . DSBlockListing , max ) ; } getTxBlock ( blockNum ) { return this . provider . send ( RPCMethod . GetTxBlock , blockNum . toString ( ) ) ; } getLatestTxBlock ( ) { return this . provider . send ( RPCMethod . GetLatestTxBlock ) ; } getNumTxBlocks ( ) { return this . provider . send ( RPCMethod . GetNumTxBlocks ) ; } getTxBlockRate ( ) { return this . provider . send ( RPCMethod . GetTxBlockRate ) ; } getTxBlockListing ( max ) { return this . provider . send ( RPCMethod . TxBlockListing , max ) ; } getNumTransactions ( ) { return this . provider . send ( RPCMethod . GetNumTransactions ) ; } getTransactionRate ( ) { return this . provider . send ( RPCMethod . GetTransactionRate ) ; } getCurrentMiniEpoch ( ) { return this . provider . send ( RPCMethod . GetCurrentMiniEpoch ) ; } getCurrentDSEpoch ( ) { return this . provider . send ( RPCMethod . GetCurrentDSEpoch ) ; } getPrevDifficulty ( ) { return this . provider . send ( RPCMethod . GetPrevDifficulty ) ; } getPrevDSDifficulty ( ) { return this . provider . send ( RPCMethod . GetPrevDSDifficulty ) ; } getTotalCoinSupply ( ) { return this . provider . send ( RPCMethod . GetTotalCoinSupply ) ; } getMinerInfo ( dsBlockNumber ) { return this . provider . send ( RPCMethod . GetMinerInfo , dsBlockNumber ) ; } @ sign async createTransaction ( tx , maxAttempts = GET_TX_ATTEMPTS , interval = 1000 , blockConfirm = false ) { try { const response = await this . provider . send ( RPCMethod . CreateTransaction , { ... tx . txParams , priority : tx . toDS , } ) ; if ( response . error ) { throw response . error ; } if ( blockConfirm ) { return tx . blockConfirm ( response . result . TranID , maxAttempts , interval ) ; } return tx . confirm ( response . result . TranID , maxAttempts , interval ) ; } catch ( err ) { throw err ; } } async createBatchTransaction ( signedTxList , maxAttempts = GET_TX_ATTEMPTS , interval = 1000 , blockConfirm = false ) { try { const txParamsList = [ ] ; for ( const signedTx of signedTxList ) { if ( signedTx . txParams . signature === undefined ) { throw new Error ( 'The transaction is not signed.' ) ; } txParamsList . push ( { ... signedTx . txParams , priority : signedTx . toDS , } ) ; } const response = await this . provider . sendBatch ( RPCMethod . CreateTransaction , txParamsList ) ; if ( response . error ) { throw response . error ; } const batchResults = [ ] ; for ( let i = 0 ; i < signedTxList . length ; i ++ ) { const tx = signedTxList [ i ] ; const txRes = response . batch_result [ i ] ; if ( blockConfirm ) { batchResults . push ( await tx . blockConfirm ( txRes . result . TranID , maxAttempts , interval ) ) ; } else { batchResults . push ( await tx . confirm ( txRes . result . TranID , maxAttempts , interval ) ) ; } } return batchResults ; } catch ( err ) { throw err ; } } async createTransactionRaw ( payload ) { try { const tx = JSON . parse ( payload ) ; const response = await this . provider . send ( RPCMethod . CreateTransaction , tx ) ; if ( response . error ) { throw response . error ; } return response . result . TranID ; } catch ( err ) { throw err ; } } @ sign async createTransactionWithoutConfirm ( tx ) { try { const response = await this . provider . send ( RPCMethod . CreateTransaction , { ... tx . txParams , priority : tx . toDS , } ) ; if ( response . error ) { throw response . error ; } tx . id = response . result . TranID ; return tx ; } catch ( err ) { throw err ; } } async createBatchTransactionWithoutConfirm ( signedTxList ) { try { const txParamsList = [ ] ; for ( const signedTx of signedTxList ) { if ( signedTx . txParams . signature === undefined ) { throw new Error ( 'The transaction is not signed.' ) ; } txParamsList . push ( { ... signedTx . txParams , priority : signedTx . toDS , } ) ; } const response = await this . provider . sendBatch ( RPCMethod . CreateTransaction , txParamsList ) ; if ( response . error ) { throw response . error ; } const batchResults = [ ] ; for ( let i = 0 ; i < signedTxList . length ; i ++ ) { const tx = signedTxList [ i ] ; const txRes = response . batch_result [ i ] ; tx . id = txRes . result . TranID ; batchResults . push ( tx ) ; } return batchResults ; } catch ( err ) { throw err ; } } async getTransaction ( txHash ) { try { const response = await this . provider . send < TransactionObj > ( RPCMethod . GetTransaction , txHash . replace ( '0x' , '' ) ) ; if ( response . error ) { return Promise . reject ( response . error ) ; } return response . result . receipt . success ? Transaction . confirm ( toTxParams ( response ) , this . provider ) : Transaction . reject ( toTxParams ( response ) , this . provider ) ; } catch ( err ) { throw err ; } } async getTransactionStatus ( txHash ) { try { const response = await this . provider . send < TransactionStatusObj > ( RPCMethod . GetTransactionStatus , txHash . replace ( '0x' , '' ) ) ; if ( response . error ) { return Promise . reject ( response . error ) ; } const modificationState = response . result . modificationState ; const status = response . result . status ; response . result . statusMessage = this . transactionStatusMap [ modificationState ] [ status ] ; return response . result ; } catch ( err ) { throw err ; } } getRecentTransactions ( ) { return this . provider . send ( RPCMethod . GetRecentTransactions ) ; } getTransactionsForTxBlock ( txBlock ) { return this . provider . send ( RPCMethod . GetTransactionsForTxBlock , txBlock . toString ( ) ) ; } getTransactionsForTxBlockEx ( txBlock ) { if ( ! isBlockNumber ( txBlock ) ) { throw new Error ( 'invalid txBlock' ) ; } return this . provider . send ( RPCMethod . GetTransactionsForTxBlockEx , txBlock . toString ( ) ) ; } getTxnBodiesForTxBlock ( txBlock ) { return this . provider . send ( RPCMethod . GetTxnBodiesForTxBlock , txBlock . toString ( ) ) ; } getTxnBodiesForTxBlockEx ( txBlock ) { if ( ! isBlockNumber ( txBlock ) ) { throw new Error ( 'invalid txBlock' ) ; } return this . provider . send ( RPCMethod . GetTxnBodiesForTxBlockEx , txBlock . toString ( ) ) ; } getNumTxnsTxEpoch ( epoch ) { return this . provider . send ( RPCMethod . GetNumTxnsTxEpoch , epoch ) ; } getNumTxnsDSEpoch ( epoch ) { return this . provider . send ( RPCMethod . GetNumTxnsDSEpoch , epoch ) ; } getMinimumGasPrice ( ) { return this . provider . send < string , string > ( RPCMethod . GetMinimumGasPrice ) ; } getBalance ( addr ) { const address = validation . isBech32 ( addr ) ? fromBech32Address ( addr ) : addr ; return this . provider . send ( RPCMethod . GetBalance , address . replace ( '0x' , '' ) . toLowerCase ( ) ) ; } getSmartContractCode ( addr ) { const address = validation . isBech32 ( addr ) ? fromBech32Address ( addr ) : addr ; return this . provider . send ( RPCMethod . GetSmartContractCode , address . replace ( '0x' , '' ) . toLowerCase ( ) ) ; } getSmartContractInit ( addr ) { const address = validation . isBech32 ( addr ) ? fromBech32Address ( addr ) : addr ; return this . provider . send ( RPCMethod . GetSmartContractInit , address . replace ( '0x' , '' ) . toLowerCase ( ) ) ; } getSmartContractState ( addr ) { const address = validation . isBech32 ( addr ) ? fromBech32Address ( addr ) : addr ; return this . provider . send ( RPCMethod . GetSmartContractState , address . replace ( '0x' , '' ) . toLowerCase ( ) ) ; } getSmartContractSubState ( addr , variableName , indices ? ) { const address = validation . isBech32 ( addr ) ? fromBech32Address ( addr ) : addr ; if ( ! variableName ) { throw new Error ( 'Variable name required' ) ; } return this . provider . send ( RPCMethod . GetSmartContractSubState , address . replace ( '0x' , '' ) . toLowerCase ( ) , variableName , indices === undefined ? [ ] : indices ) ; } getSmartContractSubStateBatch ( reqs ) { return this . provider . sendBatch ( RPCMethod . GetSmartContractSubState , reqs ) ; } getSmartContracts ( addr ) { const address = validation . isBech32 ( addr ) ? fromBech32Address ( addr ) : addr ; return this . provider . send ( RPCMethod . GetSmartContracts , address . replace ( '0x' , '' ) . toLowerCase ( ) ) ; } getContractAddressFromTransactionID ( txHash ) { return this . provider . send ( RPCMethod . GetContractAddressFromTransactionID , txHash ) ; } getStateProof ( contractAddress , sha256Hash , txBlock ) { const address = validation . isBech32 ( contractAddress ) ? fromBech32Address ( contractAddress ) : contractAddress ; const isLatestStr = txBlock === 'latest' ; const isValid = isLatestStr || isBlockNumber ( Number ( txBlock ) ) ; if ( ! isValid ) { throw new Error ( 'invalid txBlock' ) ; } return this . provider . send ( RPCMethod . GetStateProof , address . replace ( '0x' , '' ) . toLowerCase ( ) , sha256Hash , txBlock . toString ( ) ) ; } }