UNPKG

@faast/tron-payments

Version:

Library to assist in processing tron payments, such as deriving addresses and sweeping funds

204 lines (163 loc) 5.12 kB
// Many parts of this code are snippets from tronWeb: // https://github.com/tronprotocol/tron-web/blob/master/src/index.js import { BIP32Interface as HDNode, fromBase58, fromSeed } from 'bip32' import { keccak256 } from 'js-sha3' import jsSHA from 'jssha' import { ec as EC } from 'elliptic' import crypto from 'crypto' import { encode58 } from './base58' import { isValidXpub, isValidXprv } from './helpers' const ec = new EC('secp256k1') export const derivationPath = "m/44'/195'/0'" const derivationPathParts = derivationPath.split('/').slice(1) type HDKey<K> = { depth: number derive: (path: string | number, hardened?: boolean) => K } export function deriveAddress(xpub: string, index: number): string { if (!isValidXpub(xpub)) { throw new Error('Invalid xpub') } const key = fromBase58(xpub) const derived = deriveBasePath(key) .derive(0) .derive(index) return hdPublicKeyToAddress(derived) } export function derivePrivateKey(xprv: string, index: number): string { if (!isValidXprv(xprv)) { throw new Error('Invalid xprv') } const key = fromBase58(xprv) const derived = deriveBasePath(key) .derive(0) .derive(index) return hdPrivateKeyToPrivateKey(derived) } export function xprvToXpub(xprv: string | HDNode): string { const key = typeof xprv === 'string' ? fromBase58(xprv) : xprv const derivedPubKey = deriveBasePath(key) return derivedPubKey.neutered().toBase58() } export function generateNewKeys(): { xpub: string; xprv: string } { const key = fromSeed(crypto.randomBytes(32)) const xprv = key.toBase58() const xpub = xprvToXpub(xprv) return { xprv, xpub, } } // HELPER FUNCTIONS function deriveBasePath(key: HDNode): HDNode { const parts = derivationPathParts.slice(key.depth) if (parts.length > 0) { return key.derivePath(`m/${parts.join('/')}`) } return key } function hdPublicKeyToAddress(key: HDNode): string { return addressBytesToB58CheckAddress(pubBytesToTronBytes(bip32PublicToTronPublic(key.publicKey))) } function hdPrivateKeyToPrivateKey(key: HDNode): string { if (key.isNeutered() || typeof key.privateKey === 'undefined') { throw new Error('Invalid HD private key, must not be neutered') } return bip32PrivateToTronPrivate(key.privateKey) } function bip32PublicToTronPublic(pubKey: any): number[] { const pubkey = ec.keyFromPublic(pubKey).getPublic() const x = pubkey.getX() const y = pubkey.getY() let xHex = x.toString('hex') while (xHex.length < 64) { xHex = `0${xHex}` } let yHex = y.toString('hex') while (yHex.length < 64) { yHex = `0${yHex}` } const pubkeyHex = `04${xHex}${yHex}` const pubkeyBytes = hexStr2byteArray(pubkeyHex) return pubkeyBytes } function bip32PrivateToTronPrivate(priKeyBytes: Buffer): string { const key = ec.keyFromPrivate(priKeyBytes, 'bytes') let priKeyHex = key.getPrivate('hex') while (priKeyHex.length < 64) { priKeyHex = `0${priKeyHex}` } let privArray = hexStr2byteArray(priKeyHex) return byteArray2hexStr(privArray) } // Borrowed from tronweb: https://github.com/tronprotocol/tron-web/blob/master/src/utils/code.js const ADDRESS_PREFIX = '41' function byte2hexStr(byte: number): string { const hexByteMap = '0123456789ABCDEF' let str = '' str += hexByteMap.charAt(byte >> 4) str += hexByteMap.charAt(byte & 0x0f) return str } function hexStr2byteArray(str: string): number[] { const byteArray = Array() let d = 0 let j = 0 let k = 0 for (let i = 0; i < str.length; i++) { const c = str.charAt(i) if (isHexChar(c)) { d <<= 4 d += hexChar2byte(c) j++ if (0 === j % 2) { byteArray[k++] = d d = 0 } } } return byteArray } function isHexChar(c: string): boolean { return (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9') } function hexChar2byte(c: string): number { let d = 0 if (c >= 'A' && c <= 'F') { d = c.charCodeAt(0) - 'A'.charCodeAt(0) + 10 } else if (c >= 'a' && c <= 'f') { d = c.charCodeAt(0) - 'a'.charCodeAt(0) + 10 } else if (c >= '0' && c <= '9') { d = c.charCodeAt(0) - '0'.charCodeAt(0) } return d } function byteArray2hexStr(byteArray: number[]): string { let str = '' for (let i = 0; i < byteArray.length; i++) { str += byte2hexStr(byteArray[i]) } return str } function pubBytesToTronBytes(pubBytes: number[]): number[] { if (pubBytes.length === 65) { pubBytes = pubBytes.slice(1) } const hash = keccak256(pubBytes).toString() const addressHex = ADDRESS_PREFIX + hash.substring(24) return hexStr2byteArray(addressHex) } function addressBytesToB58CheckAddress(addressBytes: number[]) { const hash0 = SHA256(addressBytes) const hash1 = SHA256(hash0) let checkSum = hash1.slice(0, 4) checkSum = addressBytes.concat(checkSum) return encode58(checkSum) } function SHA256(msgBytes: number[]): number[] { const shaObj = new jsSHA('SHA-256', 'HEX') const msgHex = byteArray2hexStr(msgBytes) shaObj.update(msgHex) const hashHex = shaObj.getHash('HEX') return hexStr2byteArray(hashHex) }