laksa-core-transaction
Version:
Transaction instance for laksa
267 lines (242 loc) • 6.32 kB
JavaScript
import {
encodeTransactionProto,
getAddressFromPublicKey,
getAddress,
AddressType
} from 'laksa-core-crypto'
import { sleep, TxStatus } from './util'
/**
* @class Transaction
* @description Createa a Transaction instance
* @param {Object} params - params object
* @param {Messenger} messenger - messenger instance
* @param {String} status - txstatus
* @param {Boolean} toDs - send to Shard
* @return {Transaction} = Transaction instance
*/
class Transaction {
constructor(params, messenger, status = TxStatus.Initialised, toDS = false) {
// params
this.version = params.version
this.TranID = params.TranID
this.toAddr = getAddress(
params.toAddr,
[AddressType.bech32, AddressType.checkSum],
AddressType.checkSum
)
this.nonce = params.nonce
this.pubKey = params.pubKey
this.amount = params.amount
this.code = params.code || ''
this.data = params.data || ''
this.signature = params.signature
this.gasPrice = params.gasPrice
this.gasLimit = params.gasLimit
this.receipt = params.receipt
// status
this.status = status
this.messenger = messenger
this.toDS = toDS
}
/**
* @function confirm
* @memberof Transaction
* @param {BaseTx} params
*/
static confirm(params, messenger) {
return new Transaction(params, messenger, TxStatus.Confirmed)
}
/**
*@function reject
* @memberof Transaction
* @param {BaseTx} params
*/
static reject(params, messenger) {
return new Transaction(params, messenger, TxStatus.Rejected)
}
setMessenger(messenger) {
this.messenger = messenger
}
/**
* @param {TxStatus} status
* @returns {undefined}
*/
setStatus(status) {
this.status = status
return this
}
get bytes() {
return encodeTransactionProto(this.txParams)
}
get txParams() {
try {
return {
version: this.version,
// this.messenger.setTransactionVersion(this.version),
TranID: this.TranID,
toAddr: getAddress(
this.toAddr,
[AddressType.bech32, AddressType.checkSum],
AddressType.checkSum
),
// after updated to the core, it will not slice
nonce: this.nonce,
pubKey: this.pubKey,
amount: this.amount,
gasPrice: this.gasPrice,
gasLimit: this.gasLimit,
code: this.code,
data: this.data,
signature: this.signature,
receipt: this.receipt
}
} catch (error) {
throw error
}
}
get senderAddress() {
if (!this.pubKey) {
return String(0).repeat(40)
}
return getAddressFromPublicKey(this.pubKey)
}
/**
* @function isPending
*
* @returns {boolean}
*/
isPending() {
return this.status === TxStatus.Pending
}
/**
* @function isInitialised
*
* @returns {boolean}
*/
isInitialised() {
return this.status === TxStatus.Initialised
}
/**
* @function isRejected
*
* @returns {boolean}
*/
isSigned() {
return this.status === TxStatus.Signed
}
/**
* @function isConfirmed
*
* @returns {boolean}
*/
isConfirmed() {
return this.status === TxStatus.Confirmed
}
/**
* @function isRejected
*
* @returns {boolean}
*/
isRejected() {
return this.status === TxStatus.Rejected
}
async sendTransaction() {
if (!this.signature) {
throw new Error('The Transaction has not been signed')
}
try {
const raw = this.txParams
const result = await this.messenger.send('CreateTransaction', {
...raw,
amount: raw.amount.toString(),
gasLimit: raw.gasLimit.toString(),
gasPrice: raw.gasPrice.toString(),
priority: this.toDS
})
const { TranID } = result
if (!TranID) {
throw new Error('Transaction fail')
} else {
this.TranID = TranID
this.status = TxStatus.Pending
return { transaction: this, response: result }
}
} catch (error) {
throw error
}
}
// /**
// * confirmReceipt
// *
// * Similar to the Promise API. This sets the Transaction instance to a state
// * of pending. Calling this function kicks off a passive loop that polls the
// * lookup node for confirmation on the txHash.
// *
// * The polls are performed with a linear backoff:
// *
// * `const delay = interval * attempt`
// *
// * This is a low-level method that you should generally not have to use
// * directly.
// */
/*
*
* @param {string} txHash
* @param {number} maxAttempts
* @param {number} initial interval in milliseconds
* @returns {Promise<Transaction>}
*/
async confirm(txHash, maxAttempts = 20, interval = 1000) {
this.status = TxStatus.Pending
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
try {
if (await this.trackTx(txHash)) {
return this
}
} catch (err) {
this.status = TxStatus.Rejected
throw err
}
if (attempt + 1 < maxAttempts) {
await sleep(interval * attempt)
}
}
this.status = TxStatus.Rejected
throw new Error(`The transaction is still not confirmed after ${maxAttempts} attempts.`)
}
map(fn) {
const newParams = fn(this.txParams)
this.setParams(newParams)
return this
}
setParams(params) {
this.version = params.version
this.TranID = params.TranID
this.toAddr = getAddress(
params.toAddr,
[AddressType.bech32, AddressType.checkSum],
AddressType.checkSum
)
this.nonce = params.nonce
this.pubKey = params.pubKey
this.amount = params.amount
this.code = params.code
this.data = params.data
this.signature = params.signature
this.gasPrice = params.gasPrice
this.gasLimit = params.gasLimit
this.receipt = params.receipt
}
async trackTx(txHash) {
// TODO: regex validation for txHash so we don't get garbage
const res = await this.messenger.send('GetTransaction', txHash)
if (res.responseType === 'error') {
return false
}
this.TranID = res.ID
this.receipt = res.receipt
this.status = this.receipt && this.receipt.success ? TxStatus.Confirmed : TxStatus.Rejected
return true
}
}
export { Transaction }