├── lerna.json ├── .gitignore ├── packages ├── bnb │ ├── utils │ │ ├── bnb_addr_util.js │ │ ├── types.js │ │ └── amino.js │ ├── __tests__ │ │ ├── cancelOrder.js │ │ ├── placeOrder.js │ │ ├── issueTests.js │ │ ├── send.js │ │ └── base64.js │ ├── package.json │ ├── README.md │ └── index.js └── utxo │ ├── __tests__ │ ├── ltc.js │ ├── omni.js │ ├── horizen.js │ ├── bch.js │ └── btc.js │ ├── package.json │ ├── index.js │ └── README.md ├── README.md └── package.json /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "1.0.0" 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | aws/.terraform/ 2 | node_modules/ 3 | .idea/ 4 | .vscode/ 5 | *.log 6 | *.pid 7 | *.node-* 8 | .DS_Store 9 | package-lock.json 10 | 11 | .jshintrc 12 | .watchmanconfig 13 | -------------------------------------------------------------------------------- /packages/bnb/utils/bnb_addr_util.js: -------------------------------------------------------------------------------- 1 | const bech32 = require('bech32'); 2 | 3 | /** 4 | * Encode to normal bnb addresses 5 | * @param {Buffer} buff raw buf address 6 | * @param {boolean} testnet default = false 7 | * @return {string} bnb prefixed addr 8 | */ 9 | function encodeAddress(buff, testnet= false) { 10 | const prefix = testnet? "tbnb" : "bnb"; 11 | const words = bech32.toWords(buff) 12 | return bech32.encode(prefix, words) 13 | } 14 | 15 | /** 16 | * Decodes an address in bech32 format. 17 | * @param {string} value the bech32 address to decode 18 | */ 19 | function decodeAddress(value){ 20 | const decodeAddress = bech32.decode(value) 21 | return Buffer.from(bech32.fromWords(decodeAddress.words)) 22 | } 23 | 24 | module.exports = { 25 | decodeAddress, 26 | encodeAddress 27 | }; -------------------------------------------------------------------------------- /packages/bnb/__tests__/cancelOrder.js: -------------------------------------------------------------------------------- 1 | const decoder = require('../index') 2 | 3 | test('cancelOrder', () => { 4 | let cancelOrder = 5 | 'd001f0625dee0a58166e681b0a14ba36f0fad74d8f41045463e4774f328f4af779e5120f42434853562e422d3130465f424e421a2b424133364630464144373444384634313034353436334534373734463332384634414637373945352d3239126e0a26eb5ae98721029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e1240d93fb0402b2b30e7ea08e123bb139ad68bf0a1577f38592eb22d11e127f09bbd3380f29b4bf15bdfa973454c5c8ed444f2e256e956fe98cfd21e886a946e21e5182220212001' 6 | let decodedTx = decoder.decodeCancelOrder(cancelOrder) 7 | expect(typeof decodedTx).toBe('object') 8 | expect(decodedTx).toHaveProperty('msg') 9 | expect(decodedTx).toHaveProperty('signatures') 10 | expect(decodedTx).toHaveProperty('msgType') 11 | }) 12 | -------------------------------------------------------------------------------- /packages/bnb/__tests__/placeOrder.js: -------------------------------------------------------------------------------- 1 | const decoder = require('../index') 2 | 3 | test('placeOrder', () => { 4 | let placeOrder = 5 | 'de01f0625dee0a66ce6dc0430a14ba36f0fad74d8f41045463e4774f328f4af779e5122b424133364630464144373444384634313034353436334534373734463332384634414637373945352d33331a0d4144412e422d4236335f424e42200228013080c2d72f3880c2d72f4001126e0a26eb5ae98721029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e1240851fc9542342321af63ecbba7d3ece545f2a42bad01ba32cff5535b18e54b6d3106e10b6a4525993d185a1443d9a125186960e028eabfdd8d76cf70a3a7e3100182220202001' 6 | let decodedTx = decoder.decodePlaceOrder(placeOrder) 7 | expect(typeof decodedTx).toBe('object') 8 | expect(decodedTx).toHaveProperty('msg') 9 | expect(decodedTx).toHaveProperty('signatures') 10 | expect(decodedTx).toHaveProperty('msgType') 11 | }) 12 | -------------------------------------------------------------------------------- /packages/bnb/__tests__/issueTests.js: -------------------------------------------------------------------------------- 1 | const decoder = require('../index') 2 | 3 | test('bnb issue', () => { 4 | // test case for https://github.com/antoncoding/raw-transaction-hex-decoder/issues/1 5 | const from = 'bnb126fc4gys07jl9s7gqlgvzwjyy2mazk2m3afadt' 6 | const to = 'bnb1yvy4n68rkt6qyvufrqv3d46sndljlfaa75lur3' 7 | let txn = 8 | 'yAHwYl3uCkwqLIf6CiIKFFaTiqCQf6Xyw8gH0ME6RCK30VlbEgoKA0JOQhCAwtcvEiIKFCMJWejjsvQCM4kYGRbXUJt/L6e9EgoKA0JOQhCAwtcvEmwKJuta6YchAztOCdLc4TZvGVFzR8Di8uWQc+wrWCiNMteTI+WOrxHZEkALKLqUsUE1yfgXLVMUqCfinHXlOp2TF3XuoQhPEVrU6SkjlmIB+IBhhJWJ4S8jEDx3ITNF9/4L9STfK3EDvhh/GBgaBHRlc3QgAQ==' 9 | let txnDt = Buffer.from(txn, 'base64').toString('hex') 10 | let decodedTx = decoder.decodeTransfer(txnDt) 11 | expect(decodedTx.msg[0].inputs[0].address).toBe(from) 12 | expect(decodedTx.msg[0].outputs[0].address).toBe(to) 13 | }) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raw Transaction Hex Decoder 2 | 3 | ![contributers](https://img.shields.io/github/contributors/antoncoding/crypto-hex-decoder) ![](https://img.shields.io/github/issues/antoncoding/crypto-hex-decoder) 4 | 5 | Originally forked from [marcogbarcellos](https://github.com/marcogbarcellos/transaction-hex-decoder)'s repo, now maintaining it myself. 6 | 7 | If you want this library to support other coins, please open an issue :) 8 | 9 | ## Supported coins 10 | 11 | Please find respective documentation for each coin: 12 | 13 | - [![npm](https://img.shields.io/npm/v/@crypto-hex-decoder/bnb.svg)](https://www.npmjs.com/package/@crypto-hex-decoder/bnb) [@crypto-hex-decoder/bnb](./packages/bnb) 14 | - [![npm](https://img.shields.io/npm/v/@crypto-hex-decoder/utxo.svg)](https://www.npmjs.com/package/@crypto-hex-decoder/utxo) [@crypto-hex-decoder/utxo](./packages/utxo) 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crypto-hex-decoder", 3 | "version": "2.0.0", 4 | "description": "Raw transaction decoder for different blockchains", 5 | "author": "anton", 6 | "keywords": [ 7 | "crypto", 8 | "blockchain", 9 | "address", 10 | "cryptocurrency", 11 | "transactions", 12 | "tx", 13 | "binance", 14 | "bnb", 15 | "rawtx", 16 | "decode", 17 | "raw" 18 | ], 19 | "license": "ISC", 20 | "devDependencies": { 21 | "jest": "^24.9.0", 22 | "lerna": "^3.16.4" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/antoncoding/raw-transaction-hex-decoder.git" 27 | }, 28 | "bugs": { 29 | "url": "https://github.com/antoncoding/raw-transaction-hex-decoder/issues" 30 | }, 31 | "homepage": "https://github.com/antoncoding/raw-transaction-hex-decoder#readme" 32 | } 33 | -------------------------------------------------------------------------------- /packages/utxo/__tests__/ltc.js: -------------------------------------------------------------------------------- 1 | const utxoDecoder = require("../index"); 2 | 3 | test('ltc p2wpkh', ()=>{ 4 | const ltcRawTx = '010000000001011be5a299e70b188004258e77eb66a26ede7107609b5bf75cdd2d3b82ec2d9bb201000000171600144ed20ef6efb1c1925db64648c6bc569199de2161ffffffff02a81f0b000000000017a914bdffa40b4a490984bbe6a4bbe1af6dd9cbf06a8c87649500000000000017a91443880567fe154beed716e61d4e4e3cf45453606887024730440220334e414578bc50bc85d0c65651390f8311fc83f40c4f20c4e22b765f87cb0d87022070c25ee4b3deba660ad078c597d98f1e05c1ffd004ad525ac24903f04007159c012102c44a3acfbdff560e9be4641e5679d9ef0cca3264d2e337631f4de3ae34b20ba100000000'; 5 | let decodedTx = utxoDecoder.decode(ltcRawTx); 6 | expect(decodedTx.ins.length).toBe(1) 7 | expect(decodedTx.ins[0].witness).toHaveProperty('signature') 8 | expect(decodedTx.ins[0].witness).toHaveProperty('publicKey') 9 | expect(decodedTx.ins[0].witness.hashType).toBe(1) 10 | expect(decodedTx.outs.length).toBe(2) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/utxo/__tests__/omni.js: -------------------------------------------------------------------------------- 1 | const utxoDecoder = require("../index"); 2 | 3 | test('omni p2wpkh', ()=>{ 4 | const omniRawTx = '01000000000101cae51bf0c4791c6e20ee97adf5f4c2204ad3c9d42c4df7625e2918463efd353702000000171600148a99a17ee968fb47e3a446a24a49bed1f872808bffffffff03102700000000000017a914c7f51ad81af55f18a33dc5a95e79fe3b6e472667870000000000000000166a146f6d6e69000000000000001f000000007bdbc380463b01000000000017a914e9176b11d2e4c4fb15987fe404e6cb55f70c9236870247304402202652a083badfd8697acd968e01dbe5e62ac47031c43d0e2acc62cc82ac5a546902203e9494f4535a963763f40804c5cc161483b834f9e6b36fd8bbf49c25312112340121026747a52363a3531046f5c789cb6b1d1917164e8d77b833eb63b89060bb1d04c800000000'; 5 | let decodedTx = utxoDecoder.decode(omniRawTx); 6 | expect(decodedTx.ins.length).toBe(1) 7 | expect(decodedTx.ins[0].witness).toHaveProperty('signature') 8 | expect(decodedTx.ins[0].witness).toHaveProperty('publicKey') 9 | expect(decodedTx.outs.length).toBe(3) 10 | }) 11 | -------------------------------------------------------------------------------- /packages/bnb/__tests__/send.js: -------------------------------------------------------------------------------- 1 | const decoder = require('../index') 2 | 3 | test('transfer', () => { 4 | let bnbSend = 5 | 'ce01f0625dee0a4a2a2c87fa0a210a14d1a42a815fc6a339ecd8bfcd093dd1a835f40e1312090a03424e4210e8922612210a14e0a17a3ec9ddfd1d9c8b4e17df0622c679ffa89812090a03424e4210e89226126f0a26eb5ae987210298013db8d32124d1c11570cd37f8e52297bd18ea561cf990907f7aa03e486d6c1240ee378db6506d180dee42fdc54157c562fdd4d047a9c1c33ef407af6bd435a9023a2e0ebdb3061943a88b3a434d6b2ba8a4c970db218bd38fecf9796de973a43d182720cc011a097369676e61747572652001' 6 | let decodedTx = decoder.decode(bnbSend) 7 | expect(decodedTx.msg[0].inputs[0].address).toBe('bnb16xjz4q2lc63nnmxchlxsj0w34q6lgrsnhff60l') 8 | expect(typeof decodedTx).toBe('object') 9 | expect(decodedTx).toHaveProperty('msg') 10 | expect(decodedTx).toHaveProperty('signatures') 11 | expect(decodedTx).toHaveProperty('msgType') 12 | 13 | let decodedTx2 = decoder.decodeTransfer(bnbSend) 14 | expect(JSON.stringify(decodedTx)).toBe(JSON.stringify(decodedTx2)); 15 | }, 3000) -------------------------------------------------------------------------------- /packages/bnb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@crypto-hex-decoder/bnb", 3 | "version": "2.1.0", 4 | "description": "Binance mainnet transaction hex decoder.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest -t" 8 | }, 9 | "directories": { 10 | "test": "test" 11 | }, 12 | "author": "anton", 13 | "keywords": [ 14 | "address", 15 | "cryptocurrency", 16 | "transactions", 17 | "tx", 18 | "hex", 19 | "binance", 20 | "bnb", 21 | "rawtx", 22 | "blockchain", 23 | "decode" 24 | ], 25 | "license": "ISC", 26 | "dependencies": { 27 | "bech32": "^1.1.3", 28 | "is_js": "^0.9.0", 29 | "protocol-buffers-encodings": "^1.1.0", 30 | "safe-buffer": "^5.1.2" 31 | }, 32 | "devDependencies": { 33 | "jest": "^24.9.0" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/antoncoding/crypto-hex-decoder.git" 38 | }, 39 | "bugs": { 40 | "url": "https://github.com/antoncoding/crypto-hex-decoder/issues" 41 | }, 42 | "homepage": "https://github.com/antoncoding/crypto-hex-decoder/packages/bnb#readme" 43 | } 44 | -------------------------------------------------------------------------------- /packages/utxo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@crypto-hex-decoder/utxo", 3 | "version": "2.0.0", 4 | "description": "Transaction hex decoder for Bitcoin and other UTXO-based crypto", 5 | "main": "index.js", 6 | "author": "anton", 7 | "scripts": { 8 | "test": "jest -t" 9 | }, 10 | "directories": { 11 | "test": "test" 12 | }, 13 | "keywords": [ 14 | "utxo", 15 | "address", 16 | "cryptocurrency", 17 | "transactions", 18 | "tx", 19 | "hex", 20 | "bitcoin", 21 | "litecoin", 22 | "omni", 23 | "bitcoincash", 24 | "bch", 25 | "ltc", 26 | "btc", 27 | "rawtx", 28 | "blockchain", 29 | "decode", 30 | "scriptPubKey" 31 | ], 32 | "license": "ISC", 33 | "dependencies": { 34 | "bitcoinjs-lib": "5.0.5" 35 | }, 36 | "devDependencies": { 37 | "jest": "^24.9.0" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "git+https://github.com/antoncoding/crypto-hex-decoder.git" 42 | }, 43 | "bugs": { 44 | "url": "https://github.com/antoncoding/crypto-hex-decoder/issues" 45 | }, 46 | "homepage": "https://github.com/antoncoding/crypto-hex-decoder/packages/utxo#readme" 47 | } 48 | -------------------------------------------------------------------------------- /packages/utxo/__tests__/horizen.js: -------------------------------------------------------------------------------- 1 | const utxoDecoder = require("../index"); 2 | 3 | test('zen', ()=>{ 4 | const zenRaw = "01000000039cf6a56444c2a9d554d9feeb595a832cf24c8304cc90a3a6eaf30bf5594417f5000000006a47304402207fd74d64a10e46926278b7ee75d17f66cd3e52350822b3626d61d785778f6f7402201d4fbfce23d201fac3a07c39b3461916284ad9e8781cc25e2ef10f08eaa0689f81210316e2515e82198dbf1d8171b918be2fe1de780124b1ddee6da20d41a83dddfc8fffffffffdc83b99bf11c5b8ebe80ed157c0b0b28732b9005a9aa91c5b7030a9e6557e626000000006a4730440220346bd12436c7fca706db4af5404a0c485963b26b876518f8b058944b2a02a9f9022004f0762c7b15173aa5ceb6997f3485bd2d28d5f03c5ed07ecd985eb05043e68481210316e2515e82198dbf1d8171b918be2fe1de780124b1ddee6da20d41a83dddfc8fffffffffd13512370b291e180a7c9da0287c65911321105bfbdf56a6e8c7d493b4a2a4fc000000006a47304402202bb7c62c5d4ee7e6c991941746dd36ce2ec0c54b33d45536690c49f08ad7bd90022073536311ff60c8d86f38556a89cd84774a5adaee26b5a3a232c1ac0425bc296c81210316e2515e82198dbf1d8171b918be2fe1de780124b1ddee6da20d41a83dddfc8fffffffff0240420f00000000003f76a914a408ead75e89a3cebbc043310d49de5ef6daae8388ac2096bfa34fd7b9e955d9f22dcf3c0ec33736b95169cb4894ea2a502c0f0000000003766807b46fee7001000000003f76a914e1b874d42f42b738c795eda860179bb387ff343c88ac2096bfa34fd7b9e955d9f22dcf3c0ec33736b95169cb4894ea2a502c0f0000000003766807b400000000"; 5 | let decodedTx = utxoDecoder.decode(zenRaw); 6 | expect(decodedTx.ins.length).toBe(3) 7 | expect(decodedTx.ins[2].script).toHaveProperty('signature') 8 | expect(decodedTx.ins[2].script).toHaveProperty('publicKey') 9 | expect(decodedTx.outs.length).toBe(2) 10 | expect(typeof decodedTx.outs[0].script).toBe('string') 11 | }) -------------------------------------------------------------------------------- /packages/utxo/__tests__/bch.js: -------------------------------------------------------------------------------- 1 | const utxoDecoder = require("../index"); 2 | 3 | test('bch p2pkh', ()=>{ 4 | const bchRaw = "01000000045f6f02f8cf9b1221b04850ca253379ff29be6d8f165bc059fbf169a6b1436a58000000006a47304402201acf7edeb14f511c2d8b6f819827d379aca2da51d87e9755b64413a0e6aabed302200256ae612c7f5244bfc36ef0e87de2a2b7c9b5b0cb0769dce3d96fc381ff3b66412102100dbe85cedc0651421a9d0117e363227850fffdfc9396f15989cfab26f698dcffffffff8db70621748adc2a86728cd2cf7903d146432169cec3415c964a55ad97fe933c000000006b483045022100c33d16b1a1f3c14ddc4eaa6f43d759b6abbd8d7e4435583a9274ea1588d1d5c90220438076477abbc26f96ce72cec2976ea2561771bcb5693166d2291afe6ee6f160412102100dbe85cedc0651421a9d0117e363227850fffdfc9396f15989cfab26f698dcffffffffdba70f0187a11d0674a23b20e831f41d84439caa3ffba39aab8226e8cabc7660000000006a4730440220035da2fc1651cda021be060f3d19b7da7cc57d2eea4a6b2e59e7324dabe56d8b02202d8421c521ad38561282ccbc52bee28a03e9ca29830bbbff7794e41898dddf42412102100dbe85cedc0651421a9d0117e363227850fffdfc9396f15989cfab26f698dcffffffff7400db0aa9ce34f368033ebd56c0b697c775b38805b7c056954e20df64312b5c000000006b483045022100ab63083a06c37b6f8427a50dbf4665a01482eefe353cc676dcf803322c2282c70220356e99902bacc3262f8b76a37dbaa0fee0e675a90a97406a04d0580be17b40da412102100dbe85cedc0651421a9d0117e363227850fffdfc9396f15989cfab26f698dcffffffff015fbc3600000000001976a9141b98acb2d1e2f9b19f2e5b12b87dc3a439133aae88ac00000000"; 5 | let decodedTx = utxoDecoder.decode(bchRaw); 6 | expect(decodedTx.ins.length).toBe(4) 7 | expect(decodedTx.ins[0].script).toHaveProperty('signature') 8 | expect(decodedTx.ins[0].script).toHaveProperty('publicKey') 9 | expect(decodedTx.outs.length).toBe(1) 10 | }) 11 | -------------------------------------------------------------------------------- /packages/utxo/__tests__/btc.js: -------------------------------------------------------------------------------- 1 | const utxoDecoder = require("../index"); 2 | 3 | test('btc segwit', ()=>{ 4 | const btcRawTx = '010000000001018c5eb9b0dc16998a093d7b14ade1f08c7e50e7850300f2b9c7ddbcb91940a6520000000017160014a766979873e93859be7ce57d2d0260abe8a4c081ffffffff03220200000000000017a914e9176b11d2e4c4fb15987fe404e6cb55f70c9236870000000000000000166a146f6d6e69000000000000001f0000000005f5e10056e400000000000017a914c7f51ad81af55f18a33dc5a95e79fe3b6e4726678702483045022100d5746ae871e84a30fec792d02656a156ea6ee2f1aecc62c05bfecb77b339ecef02206c2392c29918c01e3bceed93f62b448bdcf21f4a32e28e26be922f5234eb900a012102fe5ab04839d0fc726b0dc4a7e1a8104684c9e687bd64d3304b10538d7090e91900000000'; 5 | let decodedTx = utxoDecoder.decode(btcRawTx); 6 | expect(decodedTx.ins.length).toBe(1) 7 | expect(decodedTx.ins[0].witness).toHaveProperty('signature') 8 | expect(decodedTx.ins[0].witness).toHaveProperty('publicKey') 9 | expect(decodedTx.ins[0].witness.hashType).toBe(1) 10 | }) 11 | 12 | test('btc p2pkh', ()=>{ 13 | const btcRawTx = '0100000001cae51bf0c4791c6e20ee97adf5f4c2204ad3c9d42c4df7625e2918463efd3537020000006b483045022100b00341f804157ed7c564f7acc6e5cc44f115708f0f77001c0fdd38e7b2dc480a0220724c9b9162f14936b25e5c16e44827d9549f3976bb184f478c8e455e93392a650121026747a52363a3531046f5c789cb6b1d1917164e8d77b833eb63b89060bb1d04c8ffffffff0210270000000000001976a9148a99a17ee968fb47e3a446a24a49bed1f872808b88ac463b0100000000001976a9148a99a17ee968fb47e3a446a24a49bed1f872808b88ac00000000'; 14 | let decodedTx = utxoDecoder.decode(btcRawTx); 15 | expect(decodedTx.ins.length).toBe(1) 16 | expect(decodedTx.ins[0].script).toHaveProperty('signature') 17 | expect(decodedTx.ins[0].script).toHaveProperty('publicKey') 18 | expect(typeof decodedTx.outs[0].script).toBe('string') 19 | }) 20 | -------------------------------------------------------------------------------- /packages/bnb/utils/types.js: -------------------------------------------------------------------------------- 1 | module.exports={ 2 | PlaceOrder: { 3 | msg: [ 4 | { 5 | sender: Buffer.from([]), 6 | id: "", 7 | symbol: "", 8 | ordertype: 0, 9 | side: 0, 10 | price: 0, 11 | quantity: 0, 12 | timeinforce: 0, 13 | msgType: "NewOrderMsg" 14 | } 15 | ], 16 | signatures:[{ 17 | pub_key: Buffer.from([]), 18 | signature: Buffer.from([]), 19 | account_number: 0, 20 | sequence: 0 21 | }], 22 | memo: "", 23 | source: 0, 24 | data: "", 25 | msgType:"StdTx" 26 | }, 27 | CancelOrder: { 28 | msg: [{ 29 | sender: Buffer.from([]), 30 | symbol: "", 31 | refid: "", 32 | msgType: "CancelOrderMsg" 33 | }], 34 | signatures: [{ 35 | pub_key: Buffer.from([]), //Buffer 36 | signature:  Buffer.from([]), //Buffer 37 | account_number: 0, 38 | sequence: 0 39 | }], 40 | memo: "", 41 | source: 0, 42 | data: "", 43 | msgType: "StdTx" 44 | }, 45 | Transfer:{ 46 | msg:[{ 47 | inputs: 48 | [ 49 | { 50 | address:Buffer.from([]), 51 | coins:[{ 52 | denom:"", 53 | amount:0 54 | }] 55 | } 56 | ], 57 | outputs:[ 58 | { 59 | address:Buffer.from([]), 60 | coins:[{ 61 | denom:"", 62 | amount:0 63 | }] 64 | } 65 | ], 66 | msgType:"MsgSend" 67 | }], 68 | signatures:[ 69 | { 70 | pub_key:Buffer.from([]), 71 | signature:Buffer.from([]), 72 | account_number:0, 73 | sequence:0 74 | } 75 | ], 76 | memo:"", 77 | source:0, 78 | data:"", 79 | msgType:"StdTx" 80 | } 81 | } -------------------------------------------------------------------------------- /packages/utxo/index.js: -------------------------------------------------------------------------------- 1 | const bitcoinjs = require('bitcoinjs-lib'); 2 | const bscript = bitcoinjs.script 3 | 4 | /** 5 | * Decode utxo hex. 6 | * @param {string} hex 7 | */ 8 | function decode (hex) { 9 | let tx = bitcoinjs.Transaction.fromHex(hex) 10 | 11 | tx.ins.forEach(input=>{ 12 | if (input.witness.length > 0){ 13 | input.type = 'Segwit' 14 | input.witness = decodeWitness(input.witness) 15 | input.script = { 16 | hex: input.script.toString('hex') 17 | } 18 | } else { 19 | let decodedScript = bscript.toASM(input.script).split(" ") 20 | if(decodedScript.length === 2){ 21 | input.type = 'P2PKH' 22 | input.script = { 23 | signature: decodedScript[0], 24 | publicKey: decodedScript[1] 25 | } 26 | } 27 | else{ 28 | input.type = 'Unkown' 29 | intput.script = { 30 | hex: decodedScript 31 | } 32 | } 33 | } 34 | input.hash = input.hash.toString('hex') 35 | }) 36 | 37 | tx.outs.forEach(output => { 38 | output.script = bscript.toASM(output.script) 39 | }) 40 | 41 | tx.totalValue = sumOutputValue(tx) 42 | 43 | return tx; 44 | }; 45 | 46 | /** 47 | * Sum value (satoshi) in all outputs 48 | * @param {Transaction} tx 49 | * @returns {number} satoshis 50 | */ 51 | function sumOutputValue (tx){ 52 | let totalValue = 0; 53 | if (tx && tx.outs && tx.outs.length > 0) { 54 | totalValue = tx.outs.map(out => out.value).reduce(reducer) 55 | } 56 | return totalValue 57 | } 58 | 59 | /** 60 | * convert witness hex array to object 61 | * @param {Array} witness 62 | * @return {signature: string, publicKey: string, hashType:number} 63 | */ 64 | function decodeWitness(witness){ 65 | const { signature: sigBuf, hashType } = bscript.signature.decode(witness[0]) 66 | const signature = sigBuf.toString('hex') 67 | const publicKey = witness[1].toString('hex') 68 | return { signature, publicKey, hashType } 69 | } 70 | 71 | function reducer(a, b){ return a + b} 72 | 73 | module.exports.decode = decode; -------------------------------------------------------------------------------- /packages/bnb/__tests__/base64.js: -------------------------------------------------------------------------------- 1 | const decoder = require('../index') 2 | 3 | test('base64', () => { 4 | let txn = 5 | 'xAHwYl3uCk4qLIf6CiMKFOfWS4XKCqy2D0eDhU1Tz/okobYdEgsKA0JOQhCAyK+gJRIjChRWk4qgkH+l8sPIB9DBOkQit9FZWxILCgNCTkIQgMivoCUSbAom61rphyECPYFbIkkUUiC/tnsPu9KwAf/8PpcwSTCAE/FWqsJCQJASQJIUtRhIFVlytl+Ob5sobS7sERp7+7EmDrupYRS7N1KfOhUxoRFJCja7kbXXR9CPx5aSUJk3dCIh2Q+A7Cwf5lAYFiAB' 6 | let txnDt = Buffer.from(txn, 'base64').toString('hex') 7 | 8 | class Token { 9 | constructor(opts) { 10 | opts = opts || {} 11 | this.denom = opts.denom || '' 12 | this.amount = opts.amount || 0 13 | } 14 | } 15 | 16 | class Input { 17 | constructor(opts) { 18 | opts = opts || {} 19 | this.address = opts.address || Buffer.alloc(0) 20 | this.coins = opts.coins || [new Token()] 21 | } 22 | } 23 | 24 | class Output { 25 | constructor(opts) { 26 | opts = opts || {} 27 | this.address = opts.address || Buffer.alloc(0) 28 | this.coins = opts.coins || [new Token()] 29 | } 30 | } 31 | 32 | class MsgSend { 33 | constructor(opts) { 34 | opts = opts || {} 35 | this.inputs = opts.inputs || new Input() 36 | this.outputs = opts.outputs || new Output() 37 | 38 | this.msgType = 'MsgSend' 39 | } 40 | } 41 | 42 | class StdSignature { 43 | constructor(opts) { 44 | opts = opts || {} 45 | this.pub_key = opts.pub_key || Buffer.from([]) 46 | this.signature = opts.signature || Buffer.from([]) 47 | this.account_number = opts.account_number || 0 48 | this.sequence = opts.sequence || 0 49 | } 50 | } 51 | 52 | class stdTx { 53 | constructor(opts) { 54 | opts = opts || {} 55 | this.msgs = opts.msgs || [new MsgSend()] 56 | this.signatures = opts.signatures || new StdSignature() 57 | this.memo = opts.memo || '' 58 | this.source = opts.source || 0 59 | this.data = opts.data || Buffer.alloc(0) 60 | 61 | this.msgType = 'stdTx' 62 | } 63 | } 64 | 65 | const decodeType = new stdTx() 66 | let decodedTx = decoder.decodeCustomType(txnDt, decodeType) 67 | expect(typeof decodedTx).toBe('object') 68 | expect(decodedTx).toHaveProperty('msgs') 69 | expect(decodedTx).toHaveProperty('signatures') 70 | expect(decodedTx).toHaveProperty('msgType') 71 | }) 72 | -------------------------------------------------------------------------------- /packages/utxo/README.md: -------------------------------------------------------------------------------- 1 | # UTXO Raw transaction Decoder 2 | 3 | ![npm](https://img.shields.io/npm/v/@crypto-hex-decoder/utxo.svg) ![license](https://img.shields.io/npm/l/@crypto-hex-decoder/utxo) ![vulnerabilities](https://img.shields.io/snyk/vulnerabilities/npm/@crypto-hex-decoder/utxo) ![size](https://img.shields.io/bundlephobia/min/@crypto-hex-decoder/utxo) 4 | 5 | This library can be used to decode BTC, LTC, BCH, and other UTXO-based cryptocurrecy hex transaction. 6 | 7 | ## Installation 8 | 9 | ```shell 10 | npm i @crypto-hex-decoder/bnb 11 | ``` 12 | 13 | ## Tests 14 | 15 | ```shell 16 | npm test 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```javascript 22 | const utxoDecoder = require("@crypto-hex-decoder/utxo"); 23 | 24 | const btcEncodedRawTx = "010000000001018c5eb9b0dc16998a093d7b14ade1f08c7e50e7850300f2b9c7ddbcb91940a6520000000017160014a766979873e93859be7ce57d2d0260abe8a4c081ffffffff03220200000000000017a914e9176b11d2e4c4fb15987fe404e6cb55f70c9236870000000000000000166a146f6d6e69000000000000001f0000000005f5e10056e400000000000017a914c7f51ad81af55f18a33dc5a95e79fe3b6e4726678702483045022100d5746ae871e84a30fec792d02656a156ea6ee2f1aecc62c05bfecb77b339ecef02206c2392c29918c01e3bceed93f62b448bdcf21f4a32e28e26be922f5234eb900a012102fe5ab04839d0fc726b0dc4a7e1a8104684c9e687bd64d3304b10538d7090e91900000000"; 25 | const btcDecodedRawTx = utxoDecoder.decode(btcEncodedRawTx); 26 | console.log("Decoded transaction : "+JSON.stringify(btcDecodedRawTx)); 27 | /* OUTPUT Example: 28 | { 29 | "version":1, 30 | "locktime":0, 31 | "ins":[ 32 | { 33 | "hash":"8c5eb9b0dc16998a093d7b14ade1f08c7e50e7850300f2b9c7ddbcb91940a652", 34 | "index":0, 35 | "script": 36 | { 37 | "hex":"160014a766979873e93859be7ce57d2d0260abe8a4c081" 38 | }, 39 | "sequence":4294967295, 40 | "witness": 41 | { 42 | "signature":"d5746ae871e84a30fec792d02656a156ea6ee2f1aecc62c05bfecb77b339ecef6c2392c29918c01e3bceed93f62b448bdcf21f4a32e28e26be922f5234eb900a", 43 | "publicKey":"02fe5ab04839d0fc726b0dc4a7e1a8104684c9e687bd64d3304b10538d7090e919", 44 | "hashType":1 45 | }, 46 | "type":"Segwit" 47 | } 48 | ], 49 | "outs":[ 50 | { 51 | "value":546, 52 | "script":"OP_HASH160 e9176b11d2e4c4fb15987fe404e6cb55f70c9236 OP_EQUAL" 53 | }, 54 | { 55 | "value":0, 56 | "script":"OP_RETURN 6f6d6e69000000000000001f0000000005f5e100" 57 | }, 58 | { 59 | "value":58454, 60 | "script":"OP_HASH160 c7f51ad81af55f18a33dc5a95e79fe3b6e472667 OP_EQUAL" 61 | } 62 | ], 63 | "totalValue":59000 64 | } 65 | */ 66 | 67 | ``` 68 | -------------------------------------------------------------------------------- /packages/bnb/README.md: -------------------------------------------------------------------------------- 1 | # Binance Raw Transaction Decode 2 | 3 | ![npm](https://img.shields.io/npm/v/@crypto-hex-decoder/bnb.svg) ![license](https://img.shields.io/npm/l/@crypto-hex-decoder/bnb) ![vulnerabilities](https://img.shields.io/snyk/vulnerabilities/npm/@crypto-hex-decoder/bnb) ![size](https://img.shields.io/bundlephobia/min/@crypto-hex-decoder/bnb) 4 | 5 | Amino decoder for decoding binance chain hex transactions. 6 | 7 | ## Installation 8 | 9 | ```shell 10 | npm i @crypto-hex-decoder/bnb 11 | ``` 12 | 13 | ## Run Tests 14 | 15 | ```shell 16 | npm test 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```javascript 22 | 23 | const bnbDecoder = require("@crypto-hex-decoder/bnb"); 24 | let bnbSend = 'ce01f0625dee0a4a2a2c87fa0a210a14d1a42a815fc6a339ecd8bfcd093dd1a835f40e1312090a03424e4210e8922612210a14e0a17a3ec9ddfd1d9c8b4e17df0622c679ffa89812090a03424e4210e89226126f0a26eb5ae987210298013db8d32124d1c11570cd37f8e52297bd18ea561cf990907f7aa03e486d6c1240ee378db6506d180dee42fdc54157c562fdd4d047a9c1c33ef407af6bd435a9023a2e0ebdb3061943a88b3a434d6b2ba8a4c970db218bd38fecf9796de973a43d182720cc011a097369676e61747572652001'; 25 | let decodedTx = bnbDecoder.decodeTransfer(bnbSend); 26 | console.log(JSON.stringify(decodedTx)); 27 | 28 | /* 29 | {"msg": 30 | [ 31 | { 32 | "inputs": 33 | [ 34 | { 35 | "address": "bnb16xjz4q2lc63nnmxchlxsj0w34q6lgrsnhff60l" 36 | "coins": 37 | [ 38 | { 39 | "denom":"BNB", 40 | "amount":625000 41 | } 42 | ] 43 | } 44 | ], 45 | "outputs": 46 | [ 47 | { 48 | "address": "bnb1uzsh50kfmh73m8ytfcta7p3zceull2ycnttw5s", 49 | "coins": 50 | [ 51 | { 52 | "denom":"BNB", 53 | "amount":625000 54 | } 55 | ] 56 | } 57 | ], 58 | "msgType":"MsgSend" 59 | } 60 | ], 61 | "signatures": 62 | [ 63 | { 64 | "pub_key": 65 | { 66 | "type":"Buffer", 67 | "data":[235,90,233,135,33,2,152,1,61,184,211,33,36,209,193,21,112,205,55,248,229,34,151,189,24,234,86,28,249,144,144,127,122,160,62,72,109,108] 68 | }, 69 | "signature": 70 | { 71 | "type":"Buffer", 72 | "data":[238,55,141,182,80,109,24,13,238,66,253,197,65,87,197,98,253,212,208,71,169,193,195,62,244,7,175,107,212,53,169,2,58,46,14,189,179,6,25,67,168,139,58,67,77,107,43,168,164,201,112,219,33,139,211,143,236,249,121,109,233,115,164,61] 73 | }, 74 | "account_number":39, 75 | "sequence":204 76 | } 77 | ], 78 | "memo":"signature", 79 | "source":1, 80 | "data":"", 81 | "msgType":"StdTx" 82 | } 83 | */ 84 | 85 | ``` 86 | -------------------------------------------------------------------------------- /packages/bnb/index.js: -------------------------------------------------------------------------------- 1 | const unMarshalBinaryLengthPrefixed = require('./utils/amino') 2 | const addressUtil = require('./utils/bnb_addr_util') 3 | const TYPE = require('./utils/types') 4 | 5 | function decode(hex, testnet=false){ 6 | try{ 7 | return decodeTransfer(hex, testnet) 8 | } catch (error){ 9 | try{ 10 | return decodePlaceOrder(hex, testnet) 11 | } catch (error){ 12 | try{ 13 | return decodeCancelOrder(hex, testnet) 14 | } catch(error){ 15 | throw new Error('Transaction Type not supported') 16 | } 17 | } 18 | } 19 | } 20 | 21 | /** 22 | * Decode standard transfer transaction. 23 | * @param {string} hex 24 | * @param {boolean} testnet default = false 25 | **/ 26 | function decodeTransfer(hex, testnet=false) { 27 | let clonedType = clone(TYPE.Transfer) 28 | let tx = unMarshalBinaryLengthPrefixed(Buffer.from(hex, 'hex'), clonedType).val 29 | tx.msg.map(item => { 30 | item.inputs.map(input => { 31 | input.address = addressUtil.encodeAddress(input.address, testnet) 32 | }) 33 | item.outputs.map(output => { 34 | output.address = addressUtil.encodeAddress(output.address, testnet) 35 | }) 36 | }) 37 | return tx 38 | } 39 | 40 | /** 41 | * Decode bnb Place Order transaction 42 | * @param {string} hex 43 | * @param {boolean} testnet default = false 44 | */ 45 | function decodePlaceOrder(hex, testnet=false){ 46 | let clonedType = clone(TYPE.PlaceOrder) 47 | let tx = unMarshalBinaryLengthPrefixed(Buffer.from(hex, 'hex'), clonedType).val 48 | tx.msg.map(item => { 49 | item.sender = addressUtil.encodeAddress(item.sender, testnet) 50 | }) 51 | return tx 52 | } 53 | 54 | /** 55 | * Decode bnb Cancel Order transaction 56 | * @param {string} hex 57 | * @param {boolean} testnet default = false 58 | */ 59 | function decodeCancelOrder(hex, testnet=false){ 60 | let clonedType = clone(TYPE.CancelOrder) 61 | let tx = unMarshalBinaryLengthPrefixed(Buffer.from(hex, 'hex'), clonedType).val 62 | tx.msg.map(item => { 63 | item.sender = addressUtil.encodeAddress(item.sender, testnet) 64 | }) 65 | return tx 66 | } 67 | 68 | /** 69 | * @param {string} hex 70 | * @param {class} type 71 | */ 72 | function decodeCustomType(hex, type) { 73 | if (typeof type === 'object') return unMarshalBinaryLengthPrefixed(Buffer.from(hex, 'hex'), type).val 74 | else throw 'type should be an object' 75 | } 76 | 77 | /** 78 | * clone obj 79 | * @author @YordanPavlov 80 | */ 81 | function clone(obj) { 82 | if (obj == null || typeof obj != 'object') { 83 | return obj 84 | } 85 | if (Buffer.isBuffer(obj)) { 86 | return Buffer.from([]) 87 | } 88 | var temp = new obj.constructor() 89 | for (var key in obj) { 90 | temp[key] = clone(obj[key]) 91 | } 92 | return temp 93 | } 94 | 95 | module.exports.decode = decode 96 | module.exports.decodeCustomType = decodeCustomType 97 | module.exports.decodeTransfer = decodeTransfer 98 | module.exports.decodePlaceOrder = decodePlaceOrder 99 | module.exports.decodeCancelOrder = decodeCancelOrder -------------------------------------------------------------------------------- /packages/bnb/utils/amino.js: -------------------------------------------------------------------------------- 1 | 2 | const encodings = require('protocol-buffers-encodings') 3 | const Buffer = require("safe-buffer").Buffer 4 | const is = require("is_js") 5 | 6 | const varString = encodings.string 7 | const varBool = encodings.bool 8 | const varBytes = encodings.bytes 9 | const varint = encodings.varint 10 | 11 | const decoder = (bytes, varType) => { 12 | const val = varType.decode(bytes, 0) 13 | const offset = varType.encodingLength(val) 14 | return { val, offset } 15 | } 16 | 17 | module.exports=function unMarshalBinaryLengthPrefixed (bytes, type){ 18 | if(bytes.length === 0) 19 | throw new TypeError("Cannot decode empty bytes") 20 | 21 | // read byte-length prefix 22 | const{ offset: len } = decoder(bytes, varint) 23 | 24 | if(len < 0) 25 | throw new Error(`Error reading msg byte-length prefix: got code ${len}`) 26 | 27 | bytes = bytes.slice(len) 28 | 29 | return unMarshalBinaryBare(bytes, type) 30 | } 31 | 32 | /** 33 | * js amino UnmarshalBinaryLengthPrefixed 34 | * @param {Buffer} bytes 35 | * @param {Object} type 36 | * @returns {Object} 37 | * */ 38 | function unMarshalBinaryBare(bytes, type){ 39 | if(!is.object(type)) 40 | throw new TypeError("type should be object") 41 | 42 | if(!Buffer.isBuffer(bytes)) 43 | throw new TypeError("bytes must be buffer") 44 | 45 | if(is.array(type)) { 46 | if(!is.object(type[0])) 47 | throw new TypeError("type should be object") 48 | 49 | return decodeArrayBinary(bytes, type[0], type.length) 50 | } 51 | 52 | return decodeBinary(bytes, type) 53 | } 54 | 55 | const decodeBinary = (bytes, type, isLengthPrefixed) => { 56 | if(Buffer.isBuffer(type)) { 57 | return decoder(bytes, varBytes) 58 | } 59 | 60 | if(is.array(type)) { 61 | return decodeArrayBinary(bytes, type, type.length) 62 | } 63 | 64 | if(is.number(type)) { 65 | return decoder(bytes, varint) 66 | } 67 | 68 | if(is.boolean(type)) { 69 | return decoder(bytes, varBool) 70 | } 71 | 72 | if(is.string(type)) { 73 | return decoder(bytes, varString) 74 | } 75 | 76 | if(is.object(type)) { 77 | return decodeObjectBinary(bytes, type, isLengthPrefixed) 78 | } 79 | 80 | return 81 | } 82 | 83 | const decodeObjectBinary = (bytes, type, isLengthPrefixed) => { 84 | let objectOffset = 0 85 | 86 | // read byte-length prefix 87 | if(isLengthPrefixed){ 88 | const{ offset: len } = decoder(bytes, varint) 89 | bytes = bytes.slice(len) 90 | objectOffset += len 91 | } 92 | 93 | // If registered concrete, consume and verify prefix bytes. 94 | if(type.msgType) { 95 | bytes = bytes.slice(4) 96 | objectOffset += 4 97 | } 98 | 99 | let lastFieldNum = 0 100 | const keys = Object.keys(type) 101 | keys.forEach((key, index) => { 102 | if (key === "msgType") return 103 | if (is.array(type[key])) { 104 | const { offset, val } = decodeArrayBinary(bytes, type[key][0], type[key].length) 105 | objectOffset += offset 106 | type[key] = val 107 | bytes = bytes.slice(offset) 108 | } else { 109 | const { fieldNum, typ, offset: fieldNumLen } = decodeFieldNumberAndTyp3(bytes) 110 | 111 | //if this field is default value, continue 112 | if(index+1 < fieldNum || fieldNum < 0) return 113 | 114 | // if(fieldNum <= lastFieldNum) { 115 | // throw new Error(`encountered fieldNum: ${fieldNum}, but we have already seen fnum: ${lastFieldNum}`) 116 | // } 117 | 118 | lastFieldNum = fieldNum 119 | 120 | // if(index+1 !== fieldNum) { 121 | // throw new Error("field number is not expected") 122 | // } 123 | 124 | const typeWanted = typeToTyp3(type[key]) 125 | 126 | // if(typ !== typeWanted) { 127 | // throw new Error("field type is not expected") 128 | // } 129 | 130 | //remove 1 byte of type 131 | bytes = bytes.slice(fieldNumLen) 132 | 133 | const { val, offset } = decodeBinary(bytes, type[key], true) 134 | type[key] = val 135 | 136 | //remove decoded bytes 137 | bytes = bytes.slice(offset) 138 | objectOffset += offset + 1 139 | } 140 | }) 141 | 142 | return { val: type, offset: objectOffset } 143 | } 144 | 145 | const decodeArrayBinary = (bytes, type, len) => { 146 | const arr = [] 147 | let arrayOffset = 0 148 | let { fieldNum: fieldNumber } = decodeFieldNumberAndTyp3(bytes) 149 | 150 | for(let i=0; i 0 && bytes[0] === 0x00) continue 160 | 161 | const { offset, val } = decodeBinary(bytes, type, true) 162 | 163 | arr.push({...val}) 164 | bytes = bytes.slice(offset) 165 | 166 | //add 1 byte of type 167 | arrayOffset += offset + fieldNumLen 168 | fieldNumber = fieldNum 169 | } 170 | 171 | // console.log(arr) 172 | return { val: arr, offset: arrayOffset } 173 | } 174 | 175 | const decodeFieldNumberAndTyp3 = (bytes) => { 176 | if(bytes.length < 2) { 177 | //default value 178 | return { fieldNum: -1 } 179 | } 180 | const { val, offset } = decoder(bytes, varint) 181 | const typ = val & 7 182 | let fieldNum = val >> 3 183 | if(fieldNum > (1<<29 -1)) { 184 | throw new Error(`invalid field num ${fieldNum}`) 185 | } 186 | return { fieldNum, typ, offset } 187 | } 188 | 189 | const typeToTyp3 = type => { 190 | if(is.boolean(type)){ 191 | return 0 192 | } 193 | 194 | if(is.number(type)){ 195 | if(is.integer(type)){ 196 | return 0 197 | }else{ 198 | return 1 199 | } 200 | } 201 | 202 | if(is.string(type) || is.array(type) || is.object(type)){ 203 | return 2 204 | } 205 | } --------------------------------------------------------------------------------