caver-js
Version:
caver-js is a JavaScript API library that allows developers to interact with a Klaytn node
206 lines (182 loc) • 8.13 kB
JavaScript
/*
Copyright 2020 The caver-js Authors
This file is part of the caver-js library.
The caver-js library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The caver-js library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the caver-js. If not, see <http://www.gnu.org/licenses/>.
*/
const _ = require('lodash')
const RLP = require('eth-lib/lib/rlp')
const Bytes = require('eth-lib/lib/bytes')
const AbstractTransaction = require('../abstractTransaction')
const { TX_TYPE_STRING, TX_TYPE_TAG, isNot } = require('../../transactionHelper/transactionHelper')
const Account = require('../../../../caver-account/src')
const utils = require('../../../../caver-utils/src')
function _decode(rlpEncoded) {
rlpEncoded = utils.addHexPrefix(rlpEncoded)
if (!rlpEncoded.startsWith(TX_TYPE_TAG.TxTypeAccountUpdate))
throw new Error(`Cannot decode to AccountUpdate. The prefix must be ${TX_TYPE_TAG.TxTypeAccountUpdate}: ${rlpEncoded}`)
const typeDettached = `0x${rlpEncoded.slice(4)}`
const [nonce, gasPrice, gas, from, rlpEncodedKey, signatures] = RLP.decode(typeDettached)
return {
nonce: utils.trimLeadingZero(nonce),
gasPrice: utils.trimLeadingZero(gasPrice),
gas: utils.trimLeadingZero(gas),
from,
rlpEncodedKey,
signatures,
}
}
/**
* Represents an account update transaction.
* Please refer to {@link https://docs.klaytn.com/klaytn/design/transactions/basic#txtypeaccountupdate|AccountUpdate} to see more detail.
* @class
* @hideconstructor
* @augments AbstractTransaction
*/
class AccountUpdate extends AbstractTransaction {
/**
* Creates an account update transaction.
* @method create
* @param {object|string} createTxObj - The parameters to create an AccountUpdate transaction. This can be an object defining transaction information, or it can be an RLP-encoded string.
* If it is an RLP-encoded string, decode it to create a transaction instance.
* The object can define `from`, `account`, `nonce`, `gas`, `gasPrice`, `signatures` and `chainId`.
* @param {object} [klaytnCall] - An object includes klay rpc calls.
* @return {AccountUpdate}
*/
static create(createTxObj, klaytnCall) {
return new AccountUpdate(createTxObj, klaytnCall)
}
/**
* decodes the RLP-encoded string and returns an AccountUpdate transaction instance.
*
* @param {string} rlpEncoded The RLP-encoded account update transaction.
* @param {object} [klaytnCall] - An object includes klay rpc calls.
* @return {AccountUpdate}
*/
static decode(rlpEncoded, klaytnCall) {
const decoded = _decode(rlpEncoded)
decoded.account = Account.createFromRLPEncoding(decoded.from, decoded.rlpEncodedKey)
return new AccountUpdate(decoded, klaytnCall)
}
/**
* Creates an account update transaction.
* @constructor
* @param {object|string} createTxObj - The parameters to create an AccountUpdate transaction. This can be an object defining transaction information, or it can be an RLP-encoded string.
* If it is an RLP-encoded string, decode it to create a transaction instance.
* The object can define `from`, `account`, `nonce`, `gas`, `gasPrice`, `signatures` and `chainId`.
* @param {object} [klaytnCall] - An object includes klay rpc calls.
*/
constructor(createTxObj, klaytnCall) {
if (_.isString(createTxObj)) {
createTxObj = _decode(createTxObj)
createTxObj.account = Account.createFromRLPEncoding(createTxObj.from, createTxObj.rlpEncodedKey)
}
super(TX_TYPE_STRING.TxTypeAccountUpdate, createTxObj, klaytnCall)
this.account = createTxObj.account
if (createTxObj.gasPrice !== undefined) this.gasPrice = createTxObj.gasPrice
}
/**
* @type {string}
*/
get gasPrice() {
return this._gasPrice
}
set gasPrice(g) {
this._gasPrice = utils.numberToHex(g)
}
/**
* @type {Account}
*/
get account() {
return this._account
}
set account(acct) {
if (!(acct instanceof Account)) throw new Error(`Invalid account. 'account' should be instance of 'Account'.`)
if (this.from.toLowerCase() !== acct.address.toLowerCase())
throw new Error(`Transaction from address(${this.from.toLowerCase()}) and account address(${acct.address}) do not match.`)
this._account = acct
}
/**
* Returns the RLP-encoded string of this transaction (i.e., rawTransaction).
*
* @example
* const result = tx.getRLPEncoding()
*
* @return {string} An RLP-encoded transaction string.
*/
getRLPEncoding() {
this.validateOptionalValues()
const signatures = this.signatures.map(sig => sig.encode())
return (
TX_TYPE_TAG.TxTypeAccountUpdate +
RLP.encode([
Bytes.fromNat(this.nonce),
Bytes.fromNat(this.gasPrice),
Bytes.fromNat(this.gas),
this.from.toLowerCase(),
this.account.getRLPEncodingAccountKey(),
signatures,
]).slice(2)
)
}
/**
* Returns the RLP-encoded string to make the signature of this transaction.
* This method has to be overrided in classes which extends AbstractTransaction.
* getCommonRLPEncodingForSignature is used in getRLPEncodingForSignature.
*
* @example
* const result = tx.getCommonRLPEncodingForSignature()
*
* @return {string} An RLP-encoded transaction string without signature.
*/
getCommonRLPEncodingForSignature() {
this.validateOptionalValues()
return RLP.encode([
TX_TYPE_TAG.TxTypeAccountUpdate,
Bytes.fromNat(this.nonce),
Bytes.fromNat(this.gasPrice),
Bytes.fromNat(this.gas),
this.from.toLowerCase(),
this.account.getRLPEncodingAccountKey(),
])
}
/**
* Fills in the optional variables in transaction.
*
* If the `gasPrice`, `nonce`, or `chainId` of the transaction are not defined, this method asks the default values for these optional variables and preset them by sending JSON RPC call to the connected Klaytn Node.
* Use {@link Klay#getGasPrice|caver.rpc.klay.getGasPrice} to get gasPrice, {@link Klay#getTransactionCount|caver.rpc.klay.getTransactionCount} to get nonce and {@link Klay#getChainId|caver.rpc.klay.getChainId} call to get chainId.
*
* @example
* await tx.fillTransaction()
*/
async fillTransaction() {
const [chainId, gasPrice, nonce] = await Promise.all([
isNot(this.chainId) ? this.getChainId() : this.chainId,
isNot(this.gasPrice) ? this.suggestGasPrice() : this.gasPrice,
isNot(this.nonce) ? this.getNonce(this.from) : this.nonce,
])
this.chainId = chainId
this.gasPrice = gasPrice
this.nonce = nonce
}
/**
* Checks that member variables that can be defined by the user are defined.
* If there is an undefined variable, an error occurs.
*
* @ignore
*/
validateOptionalValues() {
super.validateOptionalValues()
if (this.gasPrice === undefined)
throw new Error(`gasPrice is undefined. Define gasPrice in transaction or use 'transaction.fillTransaction' to fill values.`)
}
}
module.exports = AccountUpdate