packageDocumentation module import { arrayify , BN , encode , hexlify , keccak256 , sign , Signature , splitSignature , stripZeros , } from '@harmony-js/crypto' ; import { Messenger , RPCMethod } from '@harmony-js/network' ; import { defaultMessenger , TransactionBase , TxStatus } from '@harmony-js/transaction' ; import { numberToHex , Unit } from '@harmony-js/utils' ; import { TextEncoder } from 'text-encoding' ; hidden export class StakingSettings { public static PRECISION = 18 ; public static MAX_DECIMAL = 1000000000000000000 ; } hidden export enum Directive { DirectiveCreateValidator , DirectiveEditValidator , DirectiveDelegate , DirectiveUndelegate , DirectiveCollectRewards } export class StakingTransaction extends TransactionBase { private directive : Directive ; private stakeMsg : CreateValidator | EditValidator | Delegate | Undelegate | CollectRewards ; private nonce : number | string ; private gasLimit : number | string ; private gasPrice : number | string ; private chainId : number ; private rawTransaction : string ; private unsignedRawTransaction : string ; private signature : Signature ; private from : string ; constructor ( directive , stakeMsg , nonce , gasPrice , gasLimit , chainID , messenger = defaultMessenger , txStatus = TxStatus . INTIALIZED ) { super ( messenger , txStatus ) ; this . directive = directive ; this . stakeMsg = stakeMsg ; this . nonce = nonce ; this . gasLimit = gasLimit ; this . gasPrice = gasPrice ; this . rawTransaction = '0x' ; this . unsignedRawTransaction = '0x' ; this . signature = { r : '' , s : '' , recoveryParam : 0 , v : 0 , } ; this . chainId = chainID ; this . from = '0x' ; } encode ( ) { const raw = [ ] ; if ( ! this . directive ) { raw . push ( '0x' ) ; } else { raw . push ( hexlify ( this . directive ) ) ; } raw . push ( this . stakeMsg . encode ( ) ) ; if ( ! this . nonce ) { raw . push ( '0x' ) ; } else { raw . push ( hexlify ( this . nonce ) ) ; } raw . push ( hexlify ( this . gasPrice ) ) ; raw . push ( hexlify ( this . gasLimit ) ) ; if ( this . chainId != null && this . chainId !== 0 ) { raw . push ( hexlify ( this . chainId ) ) ; raw . push ( '0x' ) ; raw . push ( '0x' ) ; } return [ encode ( raw ) , raw ] ; } rlpSign ( prv ) { const [ unsignedRawTransaction , raw ] = this . encode ( ) ; this . setUnsigned ( unsignedRawTransaction ) ; const signature = sign ( keccak256 ( unsignedRawTransaction ) , prv ) ; const signed = this . getRLPSigned ( raw , signature ) ; return [ signature , signed ] ; } getRLPSigned ( raw , signature ) { const sig = splitSignature ( signature ) ; let v = 27 + ( sig . recoveryParam || 0 ) ; raw . pop ( ) ; raw . pop ( ) ; raw . pop ( ) ; v += this . chainId * 2 + 8 ; raw . push ( hexlify ( v ) ) ; raw . push ( stripZeros ( arrayify ( sig . r ) || [ ] ) ) ; raw . push ( stripZeros ( arrayify ( sig . s ) || [ ] ) ) ; return encode ( raw ) ; } public async sendTransaction ( ) { if ( this . rawTransaction === 'tx' || this . rawTransaction === undefined ) { throw new Error ( 'Transaction not signed' ) ; } if ( ! this . messenger ) { throw new Error ( 'Messenger not found' ) ; } const res = await this . messenger . send ( RPCMethod . SendRawStakingTransaction , this . rawTransaction , this . messenger . chainType , this . messenger . currentShard ) ; if ( res . isResult ( ) ) { this . id = res . result ; this . emitTransactionHash ( this . id ) ; this . setTxStatus ( TxStatus . PENDING ) ; return [ this , res . result ] ; } else if ( res . isError ( ) ) { this . emitConfirm ( `transaction failed:${ res . error . message }` ) ; this . setTxStatus ( TxStatus . REJECTED ) ; return [ this , `transaction failed:${ res . error . message }` ] ; } else { this . emitError ( 'transaction failed' ) ; throw new Error ( 'transaction failed' ) ; } } setUnsigned ( unSigned ) { this . unsignedRawTransaction = unSigned ; } setRawTransaction ( rawTransaction ) { this . rawTransaction = rawTransaction ; } setSignature ( signature ) { this . signature = { r : signature . r , s : signature . s , v : signature . v , recoveryParam : signature . recoveryParam , } ; } setNonce ( nonce ) { this . nonce = nonce ; } setFromAddress ( address ) { this . from = address ; } getUnsignedRawTransaction ( ) { return this . unsignedRawTransaction ; } getRawTransaction ( ) { return this . rawTransaction ; } getSignature ( ) { return this . signature ; } getFromAddress ( ) { return this . from ; } async confirm ( txHash , maxAttempts = 20 , interval = 1000 , shardID = this . messenger . currentShard , toShardID = 0 ) { const txConfirmed = await this . txConfirm ( txHash , maxAttempts , interval , shardID ) ; if ( shardID === toShardID ) { return txConfirmed ; } if ( txConfirmed . isConfirmed ( ) ) { const cxConfirmed = await this . cxConfirm ( txHash , maxAttempts , interval , toShardID ) ; return cxConfirmed ; } else { return txConfirmed ; } } } hidden export class Description { name : string ; identity : string ; website : string ; securityContact : string ; details : string ; constructor ( name , identity , website , securityContact , details ) { this . name = name ; this . identity = identity ; this . website = website ; this . securityContact = securityContact ; this . details = details ; } encode ( ) { const raw = [ ] ; const enc = new TextEncoder ( ) ; raw . push ( enc . encode ( this . name ) ) ; raw . push ( enc . encode ( this . identity ) ) ; raw . push ( enc . encode ( this . website ) ) ; raw . push ( enc . encode ( this . securityContact ) ) ; raw . push ( enc . encode ( this . details ) ) ; return raw ; } } hidden export class Decimal { value : BN ; constructor ( value ) { if ( value . length === 0 ) { throw new Error ( `decimal string is empty` ) ; } let value1 = value ; if ( value [ 0 ] === '-' ) { throw new Error ( `decimal fraction should be be between [0, 1]` ) ; } if ( value [ 0 ] === '+' ) { value1 = value . substr ( 1 ) ; } if ( value1 . length === 0 ) { throw new Error ( `decimal string is empty` ) ; } const spaced = value1 . split ( ' ' ) ; if ( spaced . length > 1 ) { throw new Error ( `bad decimal string` ) ; } const splitted = value1 . split ( '.' ) ; let len = 0 ; let combinedStr = splitted [ 0 ] ; if ( splitted . length === 2 ) { len = splitted [ 1 ] . length ; if ( len === 0 || combinedStr . length === 0 ) { throw new Error ( `bad decimal length` ) ; } if ( splitted [ 1 ] [ 0 ] === '-' ) { throw new Error ( `bad decimal string` ) ; } combinedStr += splitted [ 1 ] ; } else if ( splitted . length > 2 ) { throw new Error ( `too many periods to be a decimal string` ) ; } if ( len > StakingSettings . PRECISION ) { throw new Error ( `too much precision: precision should be less than ${ StakingSettings . PRECISION }` ) ; } const zerosToAdd = StakingSettings . PRECISION - len ; combinedStr += '0' . repeat ( zerosToAdd ) ; combinedStr = combinedStr . replace ( /^0+/ , '' ) ; const val = new Unit ( combinedStr ) . asWei ( ) . toWei ( ) ; if ( val . gt ( new Unit ( StakingSettings . MAX_DECIMAL . toString ( ) ) . asWei ( ) . toWei ( ) ) ) { throw new Error ( `too large decimal fraction` ) ; } this . value = val ; } encode ( ) { const raw = [ ] ; raw . push ( numberToHex ( this . value ) ) ; return raw ; } } hidden export class CommissionRate { rate : Decimal ; maxRate : Decimal ; maxChangeRate : Decimal ; constructor ( rate , maxRate , maxChangeRate ) { this . rate = rate ; this . maxRate = maxRate ; this . maxChangeRate = maxChangeRate ; } encode ( ) { const raw = [ ] ; raw . push ( this . rate . encode ( ) ) ; raw . push ( this . maxRate . encode ( ) ) ; raw . push ( this . maxChangeRate . encode ( ) ) ; return raw ; } } export class CreateValidator { validatorAddress : string ; description : Description ; commissionRates : CommissionRate ; minSelfDelegation : number ; maxTotalDelegation : number ; slotPubKeys : string [ ] ; amount : number ; constructor ( validatorAddress , description , commissionRates , minSelfDelegation , maxTotalDelegation , slotPubKeys , amount ) { this . validatorAddress = validatorAddress ; this . description = description ; this . commissionRates = commissionRates ; this . minSelfDelegation = minSelfDelegation ; this . maxTotalDelegation = maxTotalDelegation ; this . slotPubKeys = slotPubKeys ; this . amount = amount ; } encode ( ) { const raw = [ ] ; raw . push ( hexlify ( TransactionBase . normalizeAddress ( this . validatorAddress ) ) ) ; raw . push ( this . description . encode ( ) ) ; raw . push ( this . commissionRates . encode ( ) ) ; raw . push ( hexlify ( this . minSelfDelegation ) ) ; raw . push ( hexlify ( this . maxTotalDelegation ) ) ; raw . push ( this . encodeArr ( ) ) ; raw . push ( hexlify ( this . amount ) ) ; return raw ; } encodeArr ( ) { const raw = [ ] ; this . slotPubKeys . forEach ( ( pubKey ) => { raw . push ( pubKey ) ; } ) ; return raw ; } } export class EditValidator { validatorAddress : string ; description : Description ; commissionRate : Decimal ; minSelfDelegation : number ; maxTotalDelegation : number ; slotKeyToRemove : string ; slotKeyToAdd : string ; constructor ( validatorAddress , description , commissionRate , minSelfDelegation , maxTotalDelegation , slotKeyToRemove , slotKeyToAdd ) { this . validatorAddress = validatorAddress ; this . description = description ; this . commissionRate = commissionRate ; this . minSelfDelegation = minSelfDelegation ; this . maxTotalDelegation = maxTotalDelegation ; this . slotKeyToRemove = slotKeyToRemove ; this . slotKeyToAdd = slotKeyToAdd ; } encode ( ) { const raw = [ ] ; raw . push ( hexlify ( TransactionBase . normalizeAddress ( this . validatorAddress ) ) ) ; raw . push ( this . description . encode ( ) ) ; raw . push ( this . commissionRate . encode ( ) ) ; raw . push ( hexlify ( this . minSelfDelegation ) ) ; raw . push ( hexlify ( this . maxTotalDelegation ) ) ; raw . push ( this . slotKeyToRemove ) ; raw . push ( this . slotKeyToAdd ) ; return raw ; } } export class Delegate { delegatorAddress : string ; validatorAddress : string ; amount : number ; constructor ( delegatorAddress , validatorAddress , amount ) { this . delegatorAddress = delegatorAddress ; this . validatorAddress = validatorAddress ; this . amount = amount ; } encode ( ) { const raw = [ ] ; raw . push ( hexlify ( TransactionBase . normalizeAddress ( this . delegatorAddress ) ) ) ; raw . push ( hexlify ( TransactionBase . normalizeAddress ( this . validatorAddress ) ) ) ; raw . push ( hexlify ( this . amount ) ) ; return raw ; } } export class Undelegate { delegatorAddress : string ; validatorAddress : string ; amount : number ; constructor ( delegatorAddress , validatorAddress , amount ) { this . delegatorAddress = delegatorAddress ; this . validatorAddress = validatorAddress ; this . amount = amount ; } encode ( ) { const raw = [ ] ; raw . push ( hexlify ( TransactionBase . normalizeAddress ( this . delegatorAddress ) ) ) ; raw . push ( hexlify ( TransactionBase . normalizeAddress ( this . validatorAddress ) ) ) ; raw . push ( hexlify ( this . amount ) ) ; return raw ; } } export class CollectRewards { delegatorAddress : string ; constructor ( delegatorAddress ) { this . delegatorAddress = delegatorAddress ; } encode ( ) { const raw = [ ] ; raw . push ( hexlify ( TransactionBase . normalizeAddress ( this . delegatorAddress ) ) ) ; return raw ; } }