Javascript SDK of WoopChain protocol
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sdk/packages/harmony-transaction/src/utils.ts

278 lines
7.0 KiB

import {
hexToNumber,
isHex,
isAddress,
strip0x,
ChainType,
} from '@harmony-js/utils';
import {
decode,
encode,
keccak256,
hexlify,
BN,
hexZeroPad,
recoverAddress,
Signature,
getAddress,
sign,
} from '@harmony-js/crypto';
import { HttpProvider, Messenger } from '@harmony-js/network';
import { TxParams } from './types';
import { Transaction } from './transaction';
export const transactionFields = [
{ name: 'nonce', length: 32, fix: false },
{ name: 'gasPrice', length: 32, fix: false, transform: 'hex' },
{ name: 'gasLimit', length: 32, fix: false, transform: 'hex' },
{ name: 'shardID', length: 16, fix: false },
// recover it after main repo fix
{ name: 'toShardID', length: 16, fix: false },
{ name: 'to', length: 20, fix: true },
{ name: 'value', length: 32, fix: false, transform: 'hex' },
{ name: 'data', fix: false },
];
export const transactionFieldsETH = [
{ name: 'nonce', length: 32, fix: false },
{ name: 'gasPrice', length: 32, fix: false, transform: 'hex' },
{ name: 'gasLimit', length: 32, fix: false, transform: 'hex' },
{ name: 'to', length: 20, fix: true },
{ name: 'value', length: 32, fix: false, transform: 'hex' },
{ name: 'data', fix: false },
];
export const handleNumber = (value: string) => {
if (isHex(value) && value === '0x') {
return hexToNumber('0x00');
} else if (isHex(value) && value !== '0x') {
return hexToNumber(value);
} else {
return value;
}
};
export const handleAddress = (value: string): string => {
if (value === '0x') {
return '0x';
} else if (isAddress(value)) {
return value;
} else {
return '0x';
}
};
export const recover = (rawTransaction: string) => {
const transaction = decode(rawTransaction);
if (transaction.length !== 11 && transaction.length !== 8) {
throw new Error('invalid rawTransaction');
}
const tx: TxParams = {
id: '0x',
from: '0x',
rawTransaction: '0x',
unsignedRawTransaction: '0x',
nonce: new BN(strip0x(handleNumber(transaction[0]))).toNumber(),
gasPrice: new BN(strip0x(handleNumber(transaction[1]))),
gasLimit: new BN(strip0x(handleNumber(transaction[2]))),
shardID: new BN(strip0x(handleNumber(transaction[3]))).toNumber(),
toShardID: new BN(strip0x(handleNumber(transaction[4]))).toNumber(),
to:
handleAddress(transaction[5]) !== '0x'
? getAddress(handleAddress(transaction[5])).checksum
: '0x',
value: new BN(strip0x(handleNumber(transaction[6]))),
data: transaction[7],
chainId: 0,
signature: {
r: '',
s: '',
recoveryParam: 0,
v: 0,
},
};
// Legacy unsigned transaction
if (transaction.length === 8) {
tx.unsignedRawTransaction = rawTransaction;
return tx;
}
try {
tx.signature.v = new BN(strip0x(handleNumber(transaction[8]))).toNumber();
} catch (error) {
throw error;
}
tx.signature.r = hexZeroPad(transaction[9], 32);
tx.signature.s = hexZeroPad(transaction[10], 32);
if (
new BN(strip0x(handleNumber(tx.signature.r))).isZero() &&
new BN(strip0x(handleNumber(tx.signature.s))).isZero()
) {
// EIP-155 unsigned transaction
tx.chainId = tx.signature.v;
tx.signature.v = 0;
} else {
// Signed Tranasaction
tx.chainId = Math.floor((tx.signature.v - 35) / 2);
if (tx.chainId < 0) {
tx.chainId = 0;
}
let recoveryParam = tx.signature.v - 27;
const raw = transaction.slice(0, 8);
if (tx.chainId !== 0) {
raw.push(hexlify(tx.chainId));
raw.push('0x');
raw.push('0x');
recoveryParam -= tx.chainId * 2 + 8;
}
const digest = keccak256(encode(raw));
try {
const recoveredFrom = recoverAddress(digest, {
r: hexlify(tx.signature.r),
s: hexlify(tx.signature.s),
recoveryParam,
});
tx.from =
recoveredFrom === '0x' ? '0x' : getAddress(recoveredFrom).checksum;
} catch (error) {
throw error;
}
tx.rawTransaction = keccak256(rawTransaction);
}
return tx;
};
export const recoverETH = (rawTransaction: string) => {
const transaction = decode(rawTransaction);
if (transaction.length !== 9 && transaction.length !== 6) {
throw new Error('invalid rawTransaction');
}
const tx: TxParams = {
id: '0x',
from: '0x',
rawTransaction: '0x',
unsignedRawTransaction: '0x',
nonce: new BN(strip0x(handleNumber(transaction[0]))).toNumber(),
gasPrice: new BN(strip0x(handleNumber(transaction[1]))),
gasLimit: new BN(strip0x(handleNumber(transaction[2]))),
shardID: 0,
toShardID: 0,
to:
handleAddress(transaction[3]) !== '0x'
? getAddress(handleAddress(transaction[3])).checksum
: '0x',
value: new BN(strip0x(handleNumber(transaction[4]))),
data: transaction[5],
chainId: 0,
signature: {
r: '',
s: '',
recoveryParam: 0,
v: 0,
},
};
// Legacy unsigned transaction
if (transaction.length === 6) {
tx.unsignedRawTransaction = rawTransaction;
return tx;
}
try {
tx.signature.v = new BN(strip0x(handleNumber(transaction[6]))).toNumber();
} catch (error) {
throw error;
}
tx.signature.r = hexZeroPad(transaction[7], 32);
tx.signature.s = hexZeroPad(transaction[8], 32);
if (
new BN(strip0x(handleNumber(tx.signature.r))).isZero() &&
new BN(strip0x(handleNumber(tx.signature.s))).isZero()
) {
// EIP-155 unsigned transaction
tx.chainId = tx.signature.v;
tx.signature.v = 0;
} else {
// Signed Tranasaction
tx.chainId = Math.floor((tx.signature.v - 35) / 2);
if (tx.chainId < 0) {
tx.chainId = 0;
}
let recoveryParam = tx.signature.v - 27;
const raw = transaction.slice(0, 6);
if (tx.chainId !== 0) {
raw.push(hexlify(tx.chainId));
raw.push('0x');
raw.push('0x');
recoveryParam -= tx.chainId * 2 + 8;
}
const digest = keccak256(encode(raw));
try {
const recoveredFrom = recoverAddress(digest, {
r: hexlify(tx.signature.r),
s: hexlify(tx.signature.s),
recoveryParam,
});
tx.from =
recoveredFrom === '0x' ? '0x' : getAddress(recoveredFrom).checksum;
} catch (error) {
throw error;
}
tx.rawTransaction = keccak256(rawTransaction);
}
return tx;
};
export const sleep = async (ms: number) =>
new Promise((resolve) => {
setTimeout(() => resolve(), ms);
});
export enum TransactionEvents {
transactionHash = 'transactionHash',
error = 'error',
confirmation = 'confirmation',
receipt = 'receipt',
}
export const defaultMessenger = new Messenger(
new HttpProvider('http://localhost:8545'),
ChainType.Harmony,
);
export const RLPSign = (
transaction: Transaction,
prv: string,
): [Signature, string] => {
const [unsignedRawTransaction, raw] = transaction.getRLPUnsigned();
const regroup: TxParams = {
...transaction.txParams,
unsignedRawTransaction,
};
transaction.setParams(regroup);
const signature = sign(keccak256(unsignedRawTransaction), prv);
const signed = transaction.getRLPSigned(raw, signature);
return [signature, signed];
};