Skip to content

Instantly share code, notes, and snippets.

@amdev9
Last active December 5, 2018 17:12
Show Gist options
  • Save amdev9/3e1314f8cc62cd675fa5c8f7bbe97923 to your computer and use it in GitHub Desktop.
Save amdev9/3e1314f8cc62cd675fa5c8f7bbe97923 to your computer and use it in GitHub Desktop.
var Web3 = require('web3');
const EthereumTx = require('ethereumjs-tx');
const ethUtil = require('ethereumjs-util');
const rlp = require('rlp');
var txDecoder = require('ethereum-tx-decoder');
let flag = false;
let buffer;
const API_KEY = 'https://ropsten.infura.io/v3/YOUR_INFURA_KEY';
let web3 = new Web3(new Web3.providers.HttpProvider(API_KEY));
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:16384/');
const keyname = 'YOUR_KEYCHAIN_KEY_NAME';
const toAdd = '0xE8899BA12578d60e4D0683a596EDaCbC85eC18CC';
const publicKey = 'YOUR_PUBLIC_KEY_FROM_KEYCHAIN';
const fromAdd = ethUtil.publicToAddress(publicKey).toString('hex');
const valueTx = 100;
console.log('fromAddress ', fromAdd);
const rsv = (signature, chainIdHere) => {
const ret = {};
ret.r = `0x${signature.slice(0, 64)}`;
ret.s = `0x${signature.slice(64, 128)}`;
const recovery = parseInt(signature.slice(128, 130), 16);
let tmpV = recovery + 27;
if (chainIdHere > 0) {
tmpV += chainIdHere * 2 + 8;
}
ret.v = tmpV;
return ret;
}
const publishTx = async (rawhex) => {
var decodedTx = txDecoder.decodeTx(rawhex);
console.log('decodedTx: ', decodedTx);
return web3.eth.sendSignedTransaction(rawhex)
}
const signHexCommand = (hexraw) => {
return {
"command": "sign_hex",
"params": {
"transaction": hexraw,
"blockchain_type": "ethereum",
"keyname": keyname
}
}
}
ws.onopen = async () => {
console.log('ws open');
const rawHex = await buildTxSinature(
null, // signature
fromAdd,
toAdd,
valueTx
)
console.log('sign tx:', rawHex);
sendCommand(signHexCommand(rawHex));
}
function sendCommand(command) {
ws.send(JSON.stringify(command));
console.log('command:', command);
}
ws.on('message', async (response) => {
const data = JSON.parse(response);
console.log(data.result); // signature
flag = true;
const rawHex = await buildTxSinature(
data.result, // signature
fromAdd,
toAdd,
valueTx
)
console.log('keychain tx:', rawHex);
try {
await publishTx(`0x${rawHex}`);
} catch (e) {
console.log(e);
}
ws.close();
});
const buildTxSinature = async (signature, fromAddress, to, value, data = '') => {
console.log('buildTxSinature')
const nonce = await web3.eth.getTransactionCount(fromAddress);
const gasPrice = await web3.eth.getGasPrice().then(wei => Number(wei))
const chainIdHere = 3;
const draftTxParams = {
nonce,
gasPrice,
to,
value,
data,
// EIP 155 chainId - mainnet: 1, ropsten: 3, rinkeby: 4
chainId: chainIdHere
}
const gasLimit = 21000; // await web3.eth.estimateGas(draftTxParams) ||
let txParams = {
...draftTxParams,
gasLimit
}
if (signature) {
const ret = rsv(signature, chainIdHere);
txParams = { ...txParams,
...ret
};
}
console.log('tx keychain params', txParams)
class EthereumTxKeychain extends EthereumTx {
hashEncode(includeSignature) {
if (includeSignature === undefined) includeSignature = true
// EIP155 spec:
// when computing the hash of a transaction for purposes of signing or recovering,
// instead of hashing only the first six elements (ie. nonce, gasprice, startgas, to, value, data),
// hash nine elements, with v replaced by CHAIN_ID, r = 0 and s = 0
let items
if (includeSignature) {
items = this.raw
} else {
if (this._chainId > 0) {
const raw = this.raw.slice()
this.v = this._chainId
this.r = 0
this.s = 0
items = this.raw
this.raw = raw
} else {
items = this.raw.slice(0, 6)
}
}
// create hash
return rlp.encode(items)
}
}
const tx = new EthereumTxKeychain(txParams);
if (flag) {
console.log("validate sign transaction status: ", tx.validate() ? "SUCCESS" : "FAILURE");
buffer = tx.serialize()
} else {
buffer = tx.hashEncode(false);
}
const hex = buffer.toString('hex')
console.log('final hex: ', hex);
return hex;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment