adding sendTrasaction for staking and refactoring common code between Transaction and StakingTransaction to TransactionBase for code reuse

staking
Ganesha Upadhyaya 5 years ago
parent dd6286a49e
commit 66c08fbfea
  1. 3
      gulpfile.js
  2. 2
      packages/harmony-network/src/rpcMethod/rpc.ts
  3. 1
      packages/harmony-staking/package.json
  4. 73
      packages/harmony-staking/src/stakingTransaction.ts
  5. 3
      packages/harmony-staking/tsconfig.json
  6. 603
      packages/harmony-transaction/src/transaction.ts

@ -1,4 +1,4 @@
const {task, src} = require('gulp');
const { task, src } = require('gulp');
const del = require('del');
const fs = require('fs');
const path = require('path');
@ -11,6 +11,7 @@ const packages = [
'harmony-contract',
'harmony-utils',
'harmony-transaction',
'harmony-staking',
];
task('cleanBrowser', async () => {

@ -68,6 +68,8 @@ export const enum RPCMethod {
ProtocolVersion = 'hmy_protocolVersion',
// 32. hmy_getShardingStructure
GetShardingStructure = 'hmy_getShardingStructure',
// 33. hmy_sendRawStakingTransaction
SendRawStakingTransaction = 'hmy_sendRawStakingTransaction',
}
export const enum RPCErrorCode {

@ -21,6 +21,7 @@
"@harmony-js/crypto": "0.1.28",
"@harmony-js/network": "0.1.29",
"@harmony-js/utils": "0.1.28",
"@harmony-js/transaction": "0.1.29",
"text-encoding": "^0.7.0"
},
"gitHead": "775e2ba36924fcffd8d2c0b73587b549ed90bb81"

@ -7,14 +7,14 @@ import {
stripZeros,
Signature,
splitSignature,
getAddress,
HarmonyAddress,
keccak256,
sign,
BN,
} from '@harmony-js/crypto';
import { TextEncoder } from 'text-encoding';
import { Unit, numberToHex } from '@harmony-js/utils';
import { Messenger, RPCMethod } from '@harmony-js/network';
import { defaultMessenger, TxStatus, TransactionBase } from '@harmony-js/transaction';
export class StakingSettings {
public static PRECISION = 18;
@ -28,7 +28,7 @@ export const enum Directive {
DirectiveUndelegate,
}
export class StakingTransaction {
export class StakingTransaction extends TransactionBase {
private directive: Directive;
private stakeMsg: NewValidator | EditValidator | Delegate | Redelegate | Undelegate;
private nonce: number | string;
@ -50,7 +50,11 @@ export class StakingTransaction {
v: number,
r: string,
s: string,
messenger: Messenger = defaultMessenger,
txStatus = TxStatus.INTIALIZED,
) {
super(messenger, txStatus);
this.directive = directive;
this.stakeMsg = stakeMsg;
this.nonce = nonce;
@ -110,6 +114,37 @@ export class StakingTransaction {
return encode(raw);
}
async sendTransaction(): Promise<[StakingTransaction, string]> {
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,
0, // Staking tx always sent to shard 0
);
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: string) {
this.unsignedRawTransaction = unSigned;
}
@ -247,7 +282,7 @@ export class NewValidator {
raw.push(this.description.encode());
raw.push(this.commission.encode());
raw.push(hexlify(this.minSelfDelegation));
raw.push(hexlify(normalizeAddress(this.stakingAddress)));
raw.push(hexlify(TransactionBase.normalizeAddress(this.stakingAddress)));
raw.push(this.pubKey);
raw.push(hexlify(this.amount));
return raw;
@ -273,7 +308,7 @@ export class EditValidator {
encode(): any[] {
const raw: Array<string | Uint8Array | Array<string | Uint8Array>> = [];
raw.push(this.description.encode());
raw.push(hexlify(normalizeAddress(this.stakingAddress)));
raw.push(hexlify(TransactionBase.normalizeAddress(this.stakingAddress)));
raw.push(this.commissionRate.encode());
raw.push(hexlify(this.minSelfDelegation));
return raw;
@ -291,8 +326,8 @@ export class Delegate {
}
encode(): any[] {
const raw: Array<string | Uint8Array> = [];
raw.push(hexlify(normalizeAddress(this.delegatorAddress)));
raw.push(hexlify(normalizeAddress(this.validatorAddress)));
raw.push(hexlify(TransactionBase.normalizeAddress(this.delegatorAddress)));
raw.push(hexlify(TransactionBase.normalizeAddress(this.validatorAddress)));
raw.push(hexlify(this.amount));
return raw;
}
@ -316,9 +351,9 @@ export class Redelegate {
}
encode(): any[] {
const raw: Array<string | Uint8Array> = [];
raw.push(hexlify(normalizeAddress(this.delegatorAddress)));
raw.push(hexlify(normalizeAddress(this.validatorSrcAddress)));
raw.push(hexlify(normalizeAddress(this.validatorDstAddress)));
raw.push(hexlify(TransactionBase.normalizeAddress(this.delegatorAddress)));
raw.push(hexlify(TransactionBase.normalizeAddress(this.validatorSrcAddress)));
raw.push(hexlify(TransactionBase.normalizeAddress(this.validatorDstAddress)));
raw.push(hexlify(this.amount));
return raw;
}
@ -335,23 +370,9 @@ export class Undelegate {
}
encode(): any[] {
const raw: Array<string | Uint8Array> = [];
raw.push(hexlify(normalizeAddress(this.delegatorAddress)));
raw.push(hexlify(normalizeAddress(this.validatorAddress)));
raw.push(hexlify(TransactionBase.normalizeAddress(this.delegatorAddress)));
raw.push(hexlify(TransactionBase.normalizeAddress(this.validatorAddress)));
raw.push(hexlify(this.amount));
return raw;
}
}
export function normalizeAddress(address: string) {
if (address === '0x') {
return '0x';
} else if (
HarmonyAddress.isValidChecksum(address) ||
HarmonyAddress.isValidBech32(address) ||
HarmonyAddress.isValidBech32TestNet(address)
) {
return getAddress(address).checksum;
} else {
throw new Error(`Address format is not supported`);
}
}

@ -8,6 +8,7 @@
"references": [
{ "path": "../harmony-utils" },
{ "path": "../harmony-crypto" },
{ "path": "../harmony-network" }
{ "path": "../harmony-network" },
{ "path": "../harmony-transaction" }
]
}

@ -30,7 +30,7 @@ import {
recoverETH,
} from './utils';
class Transaction {
export class TransactionBase {
emitter: Emitter;
messenger: Messenger;
txStatus: TxStatus;
@ -43,225 +43,21 @@ class Transaction {
cxConfirmationCheck: number = 0;
receipt?: TransasctionReceipt;
private id: string;
private from: string;
private nonce: number | string;
private to: string;
private shardID: number | string;
private toShardID: number | string;
private gasLimit: BN;
private gasPrice: BN;
private data: string;
private value: BN;
private chainId: number;
private rawTransaction: string;
private unsignedRawTransaction: string;
private signature: Signature;
id: string;
shardID: number | string;
// constructor
constructor(
params?: TxParams | any,
messenger: Messenger = defaultMessenger,
txStatus = TxStatus.INTIALIZED,
) {
constructor(messenger: Messenger, txStatus: TxStatus) {
this.messenger = messenger;
this.txStatus = txStatus;
this.emitter = new Emitter();
// intialize transaction
this.id = params && params.id ? params.id : '0x';
this.from = params && params.from ? params.from : '0x';
this.nonce = params && params.nonce ? params.nonce : 0;
this.gasPrice =
params && params.gasPrice
? new Unit(params.gasPrice).asWei().toWei()
: new Unit(0).asWei().toWei();
this.gasLimit =
params && params.gasLimit
? new Unit(params.gasLimit).asWei().toWei()
: new Unit(0).asWei().toWei();
this.shardID =
params && params.shardID !== undefined ? params.shardID : this.messenger.currentShard;
this.toShardID =
params && params.toShardID !== undefined ? params.toShardID : this.messenger.currentShard;
this.to = params && params.to ? this.normalizeAddress(params.to) : '0x';
this.value =
params && params.value ? new Unit(params.value).asWei().toWei() : new Unit(0).asWei().toWei();
this.data = params && params.data ? params.data : '0x';
// chainid should change with different network settings
this.chainId = params && params.chainId ? params.chainId : this.messenger.chainId;
this.rawTransaction = params && params.rawTransaction ? params.rawTransaction : '0x';
this.unsignedRawTransaction =
params && params.unsignedRawTransaction ? params.unsignedRawTransaction : '0x';
this.signature =
params && params.signature
? params.signature
: {
r: '',
s: '',
recoveryParam: 0,
v: 0,
};
this.receipt = params && params.receipt ? params.receipt : undefined;
this.cxStatus = this.isCrossShard() ? TxStatus.INTIALIZED : TxStatus.NONE;
this.id = '0x';
this.shardID = this.messenger.currentShard;
}
setMessenger(messenger: Messenger) {
this.messenger = messenger;
}
getRLPUnsigned(): [string, any[]] {
const raw: Array<string | Uint8Array> = [];
// temp setting to be compatible with eth
const fields =
this.messenger.chainType === ChainType.Harmony ? transactionFields : transactionFieldsETH;
fields.forEach((field: any) => {
let value = (<any>this.txParams)[field.name] || [];
value = arrayify(
hexlify(field.transform === 'hex' ? add0xToString(value.toString(16)) : value),
);
// Fixed-width field
if (field.fix === true && field.length && value.length !== field.length && value.length > 0) {
throw new Error(`invalid length for ${field.name}`);
}
// Variable-width (with a maximum)
if (field.fix === false && field.length) {
value = stripZeros(value);
if (value.length > field.length) {
throw new Error(`invalid length for ${field.name}`);
}
}
raw.push(hexlify(value));
});
if (this.txParams.chainId != null && this.txParams.chainId !== 0) {
raw.push(hexlify(this.txParams.chainId));
raw.push('0x');
raw.push('0x');
}
return [encode(raw), raw];
}
getRLPSigned(raw: any[], signature: Signature): string {
// temp setting to be compatible with eth
const rawLength = this.messenger.chainType === ChainType.Harmony ? 11 : 9;
const sig = splitSignature(signature);
let v = 27 + (sig.recoveryParam || 0);
if (raw.length === rawLength) {
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);
}
getRawTransaction(): string {
return this.rawTransaction;
}
recover(rawTransaction: string): Transaction {
// temp setting to be compatible with eth
const recovered =
this.messenger.chainType === ChainType.Harmony
? recover(rawTransaction)
: recoverETH(rawTransaction);
this.setParams(recovered);
return this;
}
// use when using eth_sendTransaction
get txPayload() {
return {
from: this.txParams.from || '0x',
to: this.txParams.to || '0x',
shardID: this.txParams.shardID ? numberToHex(this.shardID) : '0x',
toShardID: this.txParams.toShardID ? numberToHex(this.toShardID) : '0x',
gas: this.txParams.gasLimit ? numberToHex(this.txParams.gasLimit) : '0x',
gasPrice: this.txParams.gasPrice ? numberToHex(this.txParams.gasPrice) : '0x',
value: this.txParams.value ? numberToHex(this.txParams.value) : '0x',
data: this.txParams.data || '0x',
nonce: this.txParams.nonce ? numberToHex(this.nonce) : '0x',
};
}
get txParams(): TxParams {
return {
id: this.id || '0x',
from: this.from || '',
nonce: this.nonce || 0,
gasPrice: this.gasPrice || new Unit(0).asWei().toWei(),
gasLimit: this.gasLimit || new Unit(0).asWei().toWei(),
shardID: this.shardID !== undefined ? this.shardID : this.messenger.currentShard,
toShardID: this.toShardID !== undefined ? this.toShardID : this.messenger.currentShard,
to: this.normalizeAddress(this.to) || '0x',
value: this.value || new Unit(0).asWei().toWei(),
data: this.data || '0x',
chainId: this.chainId || 0,
rawTransaction: this.rawTransaction || '0x',
unsignedRawTransaction: this.unsignedRawTransaction || '0x',
signature: this.signature || '0x',
};
}
setParams(params: TxParams) {
this.id = params && params.id ? params.id : '0x';
this.from = params && params.from ? params.from : '0x';
this.nonce = params && params.nonce ? params.nonce : 0;
this.gasPrice =
params && params.gasPrice
? new Unit(params.gasPrice).asWei().toWei()
: new Unit(0).asWei().toWei();
this.gasLimit =
params && params.gasLimit
? new Unit(params.gasLimit).asWei().toWei()
: new Unit(0).asWei().toWei();
this.shardID =
params && params.shardID !== undefined ? params.shardID : this.messenger.currentShard;
this.toShardID =
params && params.toShardID !== undefined ? params.toShardID : this.messenger.currentShard;
this.to = params && params.to ? this.normalizeAddress(params.to) : '0x';
this.value =
params && params.value ? new Unit(params.value).asWei().toWei() : new Unit(0).asWei().toWei();
this.data = params && params.data ? params.data : '0x';
this.chainId = params && params.chainId ? params.chainId : 0;
this.rawTransaction = params && params.rawTransaction ? params.rawTransaction : '0x';
this.unsignedRawTransaction =
params && params.unsignedRawTransaction ? params.unsignedRawTransaction : '0x';
this.signature =
params && params.signature
? params.signature
: {
r: '',
s: '',
recoveryParam: 0,
v: 0,
};
if (this.rawTransaction !== '0x') {
this.setTxStatus(TxStatus.SIGNED);
} else {
this.setTxStatus(TxStatus.INTIALIZED);
}
}
map(fn: any) {
const newParams = fn(this.txParams);
this.setParams(newParams);
return this;
}
setTxStatus(txStatus: TxStatus): void {
this.txStatus = txStatus;
}
@ -302,95 +98,37 @@ class Transaction {
isCxConfirmed(): boolean {
return this.getCxStatus() === TxStatus.CONFIRMED;
}
isCrossShard(): boolean {
return new BN(this.txParams.shardID).toString() !== new BN(this.txParams.toShardID).toString();
}
observed() {
return this.emitter;
}
async sendTransaction(): Promise<[Transaction, string]> {
if (this.rawTransaction === 'tx' || this.rawTransaction === undefined) {
throw new Error('Transaction not signed');
}
async trackTx(txHash: string, shardID: number | string) {
if (!this.messenger) {
throw new Error('Messenger not found');
}
// const fromShard = this.shardID;
// const toShard = this.toShardID;
// await this.messenger.setShardingProviders();
// TODO: regex validation for txHash so we don't get garbage
const res = await this.messenger.send(
RPCMethod.SendRawTransaction,
this.rawTransaction,
RPCMethod.GetTransactionReceipt,
txHash,
this.messenger.chainType,
typeof this.shardID === 'string' ? Number.parseInt(this.shardID, 10) : this.shardID,
typeof shardID === 'string' ? Number.parseInt(shardID, 10) : shardID,
);
if (res.isResult() && res.result !== null) {
this.receipt = res.result;
this.emitReceipt(this.receipt);
this.id = res.result.transactionHash;
this.confirmations += 1;
// temporarilly hard coded
if (res.isResult()) {
this.id = res.result;
this.emitTransactionHash(this.id);
this.setTxStatus(TxStatus.PENDING);
// await this.confirm(this.id, 20, 1000);
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');
}
}
async confirm(
txHash: string,
maxAttempts: number = 20,
interval: number = 1000,
shardID: number | string = this.txParams.shardID,
toShardID: number | string = this.txParams.toShardID,
) {
const txConfirmed = await this.txConfirm(txHash, maxAttempts, interval, shardID);
if (!this.isCrossShard()) {
return txConfirmed;
}
if (txConfirmed.isConfirmed()) {
const cxConfirmed = await this.cxConfirm(txHash, maxAttempts, interval, toShardID);
return cxConfirmed;
} else {
return txConfirmed;
}
}
async trackTx(txHash: string, shardID: number | string = this.shardID) {
if (!this.messenger) {
throw new Error('Messenger not found');
}
// TODO: regex validation for txHash so we don't get garbage
const res = await this.messenger.send(
RPCMethod.GetTransactionReceipt,
txHash,
this.messenger.chainType,
typeof shardID === 'string' ? Number.parseInt(shardID, 10) : shardID,
);
if (res.isResult() && res.result !== null) {
this.receipt = res.result;
this.emitReceipt(this.receipt);
this.id = res.result.transactionHash;
this.confirmations += 1;
if (this.receipt) {
if (this.receipt.status && this.receipt.status === '0x1') {
this.receipt.byzantium = true;
this.txStatus = TxStatus.CONFIRMED;
} else if (this.receipt.status && this.receipt.status === '0x0') {
this.receipt.byzantium = true;
this.txStatus = TxStatus.REJECTED;
} else if (this.receipt.status === undefined && this.receipt.root) {
this.receipt.byzantium = false;
this.txStatus = TxStatus.CONFIRMED;
if (this.receipt) {
if (this.receipt.status && this.receipt.status === '0x1') {
this.receipt.byzantium = true;
this.txStatus = TxStatus.CONFIRMED;
} else if (this.receipt.status && this.receipt.status === '0x0') {
this.receipt.byzantium = true;
this.txStatus = TxStatus.REJECTED;
} else if (this.receipt.status === undefined && this.receipt.root) {
this.receipt.byzantium = false;
this.txStatus = TxStatus.CONFIRMED;
}
return true;
} else {
@ -415,7 +153,7 @@ class Transaction {
txHash: string,
maxAttempts: number = 20,
interval: number = 1000,
shardID: number | string = this.shardID,
shardID: number | string,
) {
if (this.messenger.provider instanceof HttpProvider) {
this.txStatus = TxStatus.PENDING;
@ -480,8 +218,8 @@ class Transaction {
socketConfirm(
txHash: string,
maxAttempts: number = 20,
shardID: number | string = this.shardID,
): Promise<Transaction> {
shardID: number | string,
): Promise<TransactionBase> {
return new Promise((resolve, reject) => {
const newHeads = Promise.resolve(
new NewHeaders(
@ -573,9 +311,7 @@ class Transaction {
RPCMethod.GetBlockByNumber,
[blockNumber, true],
this.messenger.chainPrefix,
typeof this.txParams.shardID === 'string'
? Number.parseInt(this.txParams.shardID, 10)
: this.txParams.shardID,
typeof this.shardID === 'string' ? Number.parseInt(this.shardID, 10) : this.shardID,
);
if (block.isError()) {
throw block.message;
@ -590,7 +326,7 @@ class Transaction {
txHash: string,
maxAttempts: number = 20,
interval: number = 1000,
toShardID: number | string = this.txParams.toShardID,
toShardID: number | string,
) {
if (this.messenger.provider instanceof HttpProvider) {
const oldBlock = await this.getBlockNumber(toShardID);
@ -676,8 +412,8 @@ class Transaction {
socketCxConfirm(
txHash: string,
maxAttempts: number = 20,
toShardID: number | string = this.txParams.toShardID,
): Promise<Transaction> {
toShardID: number | string,
): Promise<TransactionBase> {
return new Promise((resolve, reject) => {
const newHeads = Promise.resolve(
new NewHeaders(
@ -721,7 +457,7 @@ class Transaction {
});
});
}
normalizeAddress(address: string) {
static normalizeAddress(address: string) {
if (address === '0x') {
return '0x';
} else if (
@ -735,4 +471,275 @@ class Transaction {
}
}
}
class Transaction extends TransactionBase {
private from: string;
private nonce: number | string;
private to: string;
//private shardID: number | string;
private toShardID: number | string;
private gasLimit: BN;
private gasPrice: BN;
private data: string;
private value: BN;
private chainId: number;
private rawTransaction: string;
private unsignedRawTransaction: string;
private signature: Signature;
// constructor
constructor(
params?: TxParams | any,
messenger: Messenger = defaultMessenger,
txStatus = TxStatus.INTIALIZED,
) {
super(messenger, txStatus);
// intialize transaction
this.id = params && params.id ? params.id : '0x';
this.from = params && params.from ? params.from : '0x';
this.nonce = params && params.nonce ? params.nonce : 0;
this.gasPrice =
params && params.gasPrice
? new Unit(params.gasPrice).asWei().toWei()
: new Unit(0).asWei().toWei();
this.gasLimit =
params && params.gasLimit
? new Unit(params.gasLimit).asWei().toWei()
: new Unit(0).asWei().toWei();
this.shardID =
params && params.shardID !== undefined ? params.shardID : this.messenger.currentShard;
this.toShardID =
params && params.toShardID !== undefined ? params.toShardID : this.messenger.currentShard;
this.to = params && params.to ? Transaction.normalizeAddress(params.to) : '0x';
this.value =
params && params.value ? new Unit(params.value).asWei().toWei() : new Unit(0).asWei().toWei();
this.data = params && params.data ? params.data : '0x';
// chainid should change with different network settings
this.chainId = params && params.chainId ? params.chainId : this.messenger.chainId;
this.rawTransaction = params && params.rawTransaction ? params.rawTransaction : '0x';
this.unsignedRawTransaction =
params && params.unsignedRawTransaction ? params.unsignedRawTransaction : '0x';
this.signature =
params && params.signature
? params.signature
: {
r: '',
s: '',
recoveryParam: 0,
v: 0,
};
this.receipt = params && params.receipt ? params.receipt : undefined;
this.cxStatus = this.isCrossShard() ? TxStatus.INTIALIZED : TxStatus.NONE;
}
getRLPUnsigned(): [string, any[]] {
const raw: Array<string | Uint8Array> = [];
// temp setting to be compatible with eth
const fields =
this.messenger.chainType === ChainType.Harmony ? transactionFields : transactionFieldsETH;
fields.forEach((field: any) => {
let value = (<any>this.txParams)[field.name] || [];
value = arrayify(
hexlify(field.transform === 'hex' ? add0xToString(value.toString(16)) : value),
);
// Fixed-width field
if (field.fix === true && field.length && value.length !== field.length && value.length > 0) {
throw new Error(`invalid length for ${field.name}`);
}
// Variable-width (with a maximum)
if (field.fix === false && field.length) {
value = stripZeros(value);
if (value.length > field.length) {
throw new Error(`invalid length for ${field.name}`);
}
}
raw.push(hexlify(value));
});
if (this.txParams.chainId != null && this.txParams.chainId !== 0) {
raw.push(hexlify(this.txParams.chainId));
raw.push('0x');
raw.push('0x');
}
return [encode(raw), raw];
}
getRLPSigned(raw: any[], signature: Signature): string {
// temp setting to be compatible with eth
const rawLength = this.messenger.chainType === ChainType.Harmony ? 11 : 9;
const sig = splitSignature(signature);
let v = 27 + (sig.recoveryParam || 0);
if (raw.length === rawLength) {
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);
}
getRawTransaction(): string {
return this.rawTransaction;
}
recover(rawTransaction: string): Transaction {
// temp setting to be compatible with eth
const recovered =
this.messenger.chainType === ChainType.Harmony
? recover(rawTransaction)
: recoverETH(rawTransaction);
this.setParams(recovered);
return this;
}
// use when using eth_sendTransaction
get txPayload() {
return {
from: this.txParams.from || '0x',
to: this.txParams.to || '0x',
shardID: this.txParams.shardID ? numberToHex(this.shardID) : '0x',
toShardID: this.txParams.toShardID ? numberToHex(this.toShardID) : '0x',
gas: this.txParams.gasLimit ? numberToHex(this.txParams.gasLimit) : '0x',
gasPrice: this.txParams.gasPrice ? numberToHex(this.txParams.gasPrice) : '0x',
value: this.txParams.value ? numberToHex(this.txParams.value) : '0x',
data: this.txParams.data || '0x',
nonce: this.txParams.nonce ? numberToHex(this.nonce) : '0x',
};
}
get txParams(): TxParams {
return {
id: this.id || '0x',
from: this.from || '',
nonce: this.nonce || 0,
gasPrice: this.gasPrice || new Unit(0).asWei().toWei(),
gasLimit: this.gasLimit || new Unit(0).asWei().toWei(),
shardID: this.shardID !== undefined ? this.shardID : this.messenger.currentShard,
toShardID: this.toShardID !== undefined ? this.toShardID : this.messenger.currentShard,
to: Transaction.normalizeAddress(this.to) || '0x',
value: this.value || new Unit(0).asWei().toWei(),
data: this.data || '0x',
chainId: this.chainId || 0,
rawTransaction: this.rawTransaction || '0x',
unsignedRawTransaction: this.unsignedRawTransaction || '0x',
signature: this.signature || '0x',
};
}
setParams(params: TxParams) {
this.id = params && params.id ? params.id : '0x';
this.from = params && params.from ? params.from : '0x';
this.nonce = params && params.nonce ? params.nonce : 0;
this.gasPrice =
params && params.gasPrice
? new Unit(params.gasPrice).asWei().toWei()
: new Unit(0).asWei().toWei();
this.gasLimit =
params && params.gasLimit
? new Unit(params.gasLimit).asWei().toWei()
: new Unit(0).asWei().toWei();
this.shardID =
params && params.shardID !== undefined ? params.shardID : this.messenger.currentShard;
this.toShardID =
params && params.toShardID !== undefined ? params.toShardID : this.messenger.currentShard;
this.to = params && params.to ? Transaction.normalizeAddress(params.to) : '0x';
this.value =
params && params.value ? new Unit(params.value).asWei().toWei() : new Unit(0).asWei().toWei();
this.data = params && params.data ? params.data : '0x';
this.chainId = params && params.chainId ? params.chainId : 0;
this.rawTransaction = params && params.rawTransaction ? params.rawTransaction : '0x';
this.unsignedRawTransaction =
params && params.unsignedRawTransaction ? params.unsignedRawTransaction : '0x';
this.signature =
params && params.signature
? params.signature
: {
r: '',
s: '',
recoveryParam: 0,
v: 0,
};
if (this.rawTransaction !== '0x') {
this.setTxStatus(TxStatus.SIGNED);
} else {
this.setTxStatus(TxStatus.INTIALIZED);
}
}
map(fn: any) {
const newParams = fn(this.txParams);
this.setParams(newParams);
return this;
}
isCrossShard(): boolean {
return new BN(this.txParams.shardID).toString() !== new BN(this.txParams.toShardID).toString();
}
async sendTransaction(): Promise<[Transaction, string]> {
if (this.rawTransaction === 'tx' || this.rawTransaction === undefined) {
throw new Error('Transaction not signed');
}
if (!this.messenger) {
throw new Error('Messenger not found');
}
// const fromShard = this.shardID;
// const toShard = this.toShardID;
// await this.messenger.setShardingProviders();
const res = await this.messenger.send(
RPCMethod.SendRawTransaction,
this.rawTransaction,
this.messenger.chainType,
typeof this.shardID === 'string' ? Number.parseInt(this.shardID, 10) : this.shardID,
);
// temporarilly hard coded
if (res.isResult()) {
this.id = res.result;
this.emitTransactionHash(this.id);
this.setTxStatus(TxStatus.PENDING);
// await this.confirm(this.id, 20, 1000);
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');
}
}
async confirm(
txHash: string,
maxAttempts: number = 20,
interval: number = 1000,
shardID: number | string = this.txParams.shardID,
toShardID: number | string = this.txParams.toShardID,
) {
const txConfirmed = await this.txConfirm(txHash, maxAttempts, interval, shardID);
if (!this.isCrossShard()) {
return txConfirmed;
}
if (txConfirmed.isConfirmed()) {
const cxConfirmed = await this.cxConfirm(txHash, maxAttempts, interval, toShardID);
return cxConfirmed;
} else {
return txConfirmed;
}
}
}
export { Transaction };

Loading…
Cancel
Save