Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@getsafle/vault-bitcoin-controller

Package Overview
Dependencies
Maintainers
4
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@getsafle/vault-bitcoin-controller - npm Package Compare versions

Comparing version 2.0.1 to 2.0.2

.github/workflows/other-branches.yml

15

CHANGELOG.md

@@ -50,2 +50,15 @@ ### 1.0.0 (2021-12-27)

##### Enabled message and transaction signing for imported accounts
##### Enabled message and transaction signing for imported accounts
### 2.0.2 (2023-11-18)
##### Implement get satoshi per byte functionality
- Standardize getFees() to provide transaction fee in sathoshi and transaction size
- Added HD PATH for testnet
- Updated tests to add assertions
- Added constants for base URLs
- Updated node version in CI
- Updated raw transaction paramaters, amount to be accepted in satoshi
- Added badges
- Updated license in package json

14

package.json
{
"name": "@getsafle/vault-bitcoin-controller",
"version": "2.0.1",
"version": "2.0.2",
"description": "",

@@ -12,3 +12,8 @@ "engines": {

"lint:fix": "eslint --fix . --ext .js",
"test": "mocha --timeout 15000"
"test": "mocha \"test/**.js\" --timeout 15000",
"test:coverage": "npm run cover:unit && npm run cover:report",
"test:coveragehtml": "npm run cover:unit && npm run cover:reporthtml",
"cover:unit": "nyc --silent npm run test",
"cover:report": "nyc report --reporter=lcov --reporter=text --report-dir='./jscoverage'",
"cover:reporthtml": "nyc report --reporter=html --report-dir='./jscoverage'"
},

@@ -24,3 +29,3 @@ "repository": {

"author": "",
"license": "ISC",
"license": "MIT",
"bugs": {

@@ -45,4 +50,5 @@ "url": "https://github.com/getsafle/vault-bitcoin-controller/issues"

"eslint-plugin-node": "^11.1.0",
"mocha": "^8.1.3"
"mocha": "^8.1.3",
"nyc": "^15.0.0"
}
}

@@ -1,3 +0,8 @@

# bitcoin-controller
# bitcoin-controller<code><a href="https://www.docker.com/" target="_blank"><img height="50" src="https://bitcoin.org/img/icons/logotop.svg?1700824099"></a></code>
[![npm version](https://badge.fury.io/js/@getsafle%2Fvault-bitcoin-controller.svg)](https://badge.fury.io/js/@getsafle%2Fvault-bitcoin-controller) <img alt="Static Badge" src="https://img.shields.io/badge/License-MIT-green"> [![Discussions][discussions-badge]][discussions-link]
<img alt="Static Badge" src="https://img.shields.io/badge/bitcoin_controller-documentation-purple">
## Install

@@ -58,3 +63,3 @@

```
const fees = await bitcoinController.getFee(address, satPerByte);
const fees = await bitcoinController.getFees(rawTransaction);
```

@@ -67,1 +72,4 @@

```
[discussions-badge]: https://img.shields.io/badge/Code_Quality-passing-rgba
[discussions-link]: https://github.com/getsafle/vault-bitcoin-controller/actions
module.exports = {
bitcoin: {
HD_PATH: `m/84'/0'/0'/0`,
HD_PATH_MAINNET: `m/84'/0'/0'/0`,
HD_PATH_TESTNET: `m/84'/1'/0'/0`
},

@@ -17,5 +18,3 @@ bitcoin_transaction: {

}
},
SATOSHI: 100000000,
SOCHAIN_API_KEY: 'F7RKphrzItSQV-MUoPY_S0XIuwTzxFpt'
}
}
const axios = require('axios')
var sb = require("satoshi-bitcoin");
const { SATOSHI } = require("../config/index")
async function getFeeAndInput(URL, satPerByte, headers) {
let fee = 0;
async function getTransactionSize(URL, headers){
let inputCount = 0;
let outputCount = 2;
const utxos = await axios({
url : `${URL}`,
// url: 'https://sochain.com/api/v3/unspent_outputs/BTC/bc1q7cyrfmck2ffu2ud3rn5l5a8yv6f0chkp0zpemf',
method: 'GET',
headers: headers
});
let totalAmountAvailable = 0;

@@ -33,9 +30,18 @@

transactionSize = inputCount * 180 + outputCount * 34 + 10 - inputCount;
let transactionSize = inputCount * 180 + outputCount * 34 + 10 - inputCount;
return { transactionSize, totalAmountAvailable, inputs}
}
async function getFeeAndInput(URL, satPerByte, headers) {
let { transactionSize, totalAmountAvailable, inputs} = await getTransactionSize(URL, headers)
let fee = 0;
// the fees assuming we want to pay 20 satoshis per byte
fee = transactionSize * satPerByte
return { totalAmountAvailable, inputs, fee }
return { totalAmountAvailable, inputs, fee, transactionSize}
}
module.exports = getFeeAndInput
module.exports = {
getFeeAndInput,
getTransactionSize
}
const signTransaction = require('./signTransaction')
const getFeeAndInput = require('./calculateFeeAndInput')
const {getFeeAndInput, getTransactionSize} = require('./calculateFeeAndInput')
const utils = require('./utils/index')

@@ -8,3 +8,4 @@

utils,
getFeeAndInput
getFeeAndInput,
getTransactionSize
}
const bitcore = require("bitcore-lib")
const { SATOSHI, DEFAULT_SATOSHI_PER_BYTE } = require("../config/index")
const getFeeAndInput = require('./calculateFeeAndInput')
const {getFeeAndInput} = require('./calculateFeeAndInput')
async function signTransaction(from, to, amountToSend, URL, privateKey, satPerByte, headers) {
const satoshiToSend = amountToSend * SATOSHI;
const transaction = new bitcore.Transaction();

@@ -14,3 +11,3 @@

if (totalAmountAvailable - satoshiToSend - fee < 0) {
if (totalAmountAvailable - amountToSend - fee < 0) {
throw new Error("Balance is too low for this transaction");

@@ -23,3 +20,3 @@ }

// set the recieving address and the amount to send
transaction.to(to, Math.floor(satoshiToSend));
transaction.to(to, Math.floor(amountToSend));

@@ -26,0 +23,0 @@ // Set change address - Address to receive the left over funds after transfer

const { bitcoin: { HD_PATH } } = require("../../config/index")
function calcBip32ExtendedKey(bip32RootKey) {
function calcBip32ExtendedKey(bip32RootKey, hdPath) {
// Check there's a root key to derive from

@@ -10,3 +10,3 @@ if (!bip32RootKey) {

// Derive the key from the path constant
var pathBits = HD_PATH.split("/");
var pathBits = hdPath.split("/");
for (var i = 0; i < pathBits.length; i++) {

@@ -13,0 +13,0 @@ var bit = pathBits[i];

@@ -5,7 +5,6 @@ const bitcoinjs = require('bitcoinjs-lib')

let wallet = "NA";
wallet = bip32ExtendedKey.derive(index);
let wallet = bip32ExtendedKey.derive(index);
const hasPrivkey = !wallet.isNeutered();
let privkey = "NA";
let privkey
if (hasPrivkey) {

@@ -12,0 +11,0 @@ privkey = wallet.toWIF();

@@ -11,3 +11,4 @@ const ObservableStore = require('obs-store')

const { bitcoin: { HD_PATH }, bitcoin_transaction: { NATIVE_TRANSFER }, bitcoin_network: { MAINNET, TESTNET }, SOCHAIN_API_KEY } = require('./config/index')
const { bitcoin: { HD_PATH_MAINNET, HD_PATH_TESTNET }, bitcoin_transaction: { NATIVE_TRANSFER }, bitcoin_network: { MAINNET, TESTNET }} = require('./config/index')
const { SOCHAIN_API_KEY, SOCHAIN_BASE_URL, BLOCKCYPHER_BASE_URL } = require('./constants/index')

@@ -22,3 +23,3 @@ class KeyringController {

constructor(opts) {
this.store = new ObservableStore({ mnemonic: opts.mnemonic, hdPath: HD_PATH, network: helpers.utils.getNetwork(opts.network), networkType: opts.network ? opts.network : MAINNET.NETWORK, wallet: null, address: [] })
this.store = new ObservableStore({ mnemonic: opts.mnemonic, hdPath: opts.network === TESTNET.NETWORK ? HD_PATH_TESTNET : HD_PATH_MAINNET, network: helpers.utils.getNetwork(opts.network), networkType: opts.network ? opts.network : MAINNET.NETWORK, wallet: null, address: [] })
this.generateWallet()

@@ -29,6 +30,6 @@ this.importedWallets = []

generateWallet() {
const { mnemonic, network } = this.store.getState();
const { mnemonic, network, hdPath } = this.store.getState();
const seed = bip39.mnemonicToSeed(mnemonic)
const bip32RootKey = bitcoinjs.bip32.fromSeed(seed, network);
const extendedKeys = helpers.utils.calcBip32ExtendedKeys(bip32RootKey)
const extendedKeys = helpers.utils.calcBip32ExtendedKeys(bip32RootKey, hdPath)
this.updatePersistentStore({ wallet: extendedKeys })

@@ -93,3 +94,3 @@ return extendedKeys

const URL = `https://sochain.com/api/v3/unspent_outputs/${networkType === TESTNET.NETWORK ? 'BTCTEST' : "BTC"}/${from}`
const URL = SOCHAIN_BASE_URL + `unspent_outputs/${networkType === TESTNET.NETWORK ? 'BTCTEST' : "BTC"}/${from}`
const headers = { "API-KEY": SOCHAIN_API_KEY}

@@ -138,3 +139,3 @@

method: "POST",
url: `https://chain.so/api/v3/broadcast_transaction/${networkType === TESTNET.NETWORK ? 'BTCTEST' : "BTC"}`,
url: SOCHAIN_BASE_URL + `broadcast_transaction/${networkType === TESTNET.NETWORK ? 'BTCTEST' : "BTC"}`,
data: {

@@ -151,9 +152,35 @@ tx_hex: TransactionHex,

async getFee(address, satPerByte) {
async getFees(rawTransaction) {
const { networkType } = this.store.getState()
const { from } = rawTransaction
try {
const URL = `https://sochain.com/api/v3/unspent_outputs/${networkType === TESTNET.NETWORK ? 'BTCTEST' : "BTC"}/${address}`
const URL = BLOCKCYPHER_BASE_URL + `${networkType === TESTNET.NETWORK ? 'test3' : "main"}/`
const response = await axios({
url : `${URL}`,
method: 'GET',
});
let fees = {
slow: {
satPerByte: parseInt(response.data.low_fee_per_kb/1000),
},
standard: {
satPerByte: parseInt(response.data.medium_fee_per_kb/1000),
},
fast: {
satPerByte: parseInt(response.data.high_fee_per_kb/1000)
}
}
// get transaction size
const sochainURL = SOCHAIN_BASE_URL + `unspent_outputs/${networkType === TESTNET.NETWORK ? 'BTCTEST' : "BTC"}/${from}`
const headers = { "API-KEY": SOCHAIN_API_KEY}
const { totalAmountAvailable, inputs, fee } = await helpers.getFeeAndInput(URL, satPerByte, headers)
return { transactionFees: fee }
let {transactionSize} = helpers.getTransactionSize(sochainURL, headers)
return {
transactionSize,
fees
}
} catch (err) {

@@ -180,3 +207,3 @@ throw err

try {
const URL = `https://sochain.com/api/v3/balance/${networkType === TESTNET.NETWORK ? 'BTCTEST' : "BTC"}/${address}`
const URL = SOCHAIN_BASE_URL + `balance/${networkType === TESTNET.NETWORK ? 'BTCTEST' : "BTC"}/${address}`
const headers = { "API-KEY": SOCHAIN_API_KEY}

@@ -183,0 +210,0 @@ const balance = await axios({

module.exports = {
HD_WALLET_12_MNEMONIC : 'uphold job voyage bunker attract similar ship pear soda security rubber offer',
EXTERNAL_ACCOUNT_PRIVATE_KEY_MAINNET: "L51zqNpZY6pgVz8ggXFghXRSvuub8PvsHLhQy3w4uyX1CHQgurRb", // works on MAINNET ONLY
EXTERNAL_ACCOUNT_ADDRESS_MAINNET: "bc1q7nv22l23wyu4vtq869sgzke3j9swetvytxxcla",
EXTERNAL_ACCOUNT_PRIVATE_KEY: "L2dwGhW8XfBCiJw4GQ9nmumA4APwYZVNRDxXKdPQvAVQ8zHn3VAG", // works on MAINNET ONLY
EXTERNAL_ACCOUNT_ADDRESS: "bc1q7e5yrgec5sq4xz80um9yp5kazahpu20tg9d3ta",
TEST_ADDRESS_1: 'tb1qw3rs6lte05ej6rzm86sd4rg6vt9ss27shx5z8q',
TEST_ADDRESS_2: 'tb1qt3vyrqgy7emqcr8sjr39zcjdca7grfer7lvxhp',
EXTERNAL_ACCOUNT_PRIVATE_KEY: "cQanwmov5nfBHrdqoAAV42i3Xgw8cyebAmKBLRcKXvQH5eutSQzo", // works on TESTNET ONLY
EXTERNAL_ACCOUNT_ADDRESS: "tb1qcen5y7uvp3r9y0c4l4c55hlaks8gef54pr6v68",
EXTERNAL_ACCOUNT_WRONG_PRIVATE_KEY_1: "random_private_key",

@@ -19,4 +20,4 @@ EXTERNAL_ACCOUNT_WRONG_PRIVATE_KEY_2: "0xbcb7a8680126610ca94440b020280f9ef829ad26637bfb5cc",

TRANSFER_BTC: {
BTC_RECEIVER: 'bc1qgkw48n0d74krmvq7d50nechkeqf057p7gyyl7r', // address at index 1
BTC_AMOUNT: 0.00001
BTC_RECEIVER: 'tb1qt3vyrqgy7emqcr8sjr39zcjdca7grfer7lvxhp', // address at test index 1
BTC_AMOUNT: 1000
},

@@ -23,0 +24,0 @@ BITCOIN_NETWORK: {

@@ -6,7 +6,7 @@ var assert = require('assert');

HD_WALLET_12_MNEMONIC,
TEST_ADDRESS_1,
TEST_ADDRESS_2,
HD_WALLET_12_MNEMONIC_TEST_OTHER,
EXTERNAL_ACCOUNT_PRIVATE_KEY,
EXTERNAL_ACCOUNT_ADDRESS,
EXTERNAL_ACCOUNT_PRIVATE_KEY_MAINNET,
EXTERNAL_ACCOUNT_ADDRESS_MAINNET,
EXTERNAL_ACCOUNT_WRONG_PRIVATE_KEY_1,

@@ -36,3 +36,3 @@ EXTERNAL_ACCOUNT_WRONG_PRIVATE_KEY_2,

mnemonic: HD_WALLET_12_MNEMONIC,
network: MAINNET
network: TESTNET
}

@@ -45,5 +45,5 @@

const wallet = await bitcoinWallet.addAccount()
console.log("wallet, ", wallet)
assert(wallet.address === TEST_ADDRESS_1, "Added address should be " + TEST_ADDRESS_1)
const wallet2 = await bitcoinWallet.addAccount()
console.log("wallet2, ", wallet2)
assert(wallet2.address === TEST_ADDRESS_2, "Added address should be " + TEST_ADDRESS_2)
})

@@ -53,3 +53,2 @@

const acc = await bitcoinWallet.getAccounts()
console.log("acc ", acc)
assert(acc.length === 2, "Should have 2 addresses")

@@ -60,4 +59,4 @@ })

const acc = await bitcoinWallet.getAccounts()
const privateKey = await bitcoinWallet.exportPrivateKey(acc[0])
console.log("privateKey, ", privateKey)
const result = await bitcoinWallet.exportPrivateKey(acc[0])
assert(result.privateKey)
})

@@ -69,24 +68,43 @@

const signedMessage1 = await bitcoinWallet.signMessage(TESTING_MESSAGE_1, acc[0])
console.log("Signed message 1: ", signedMessage1)
assert(bitcoinMessage.verify(TESTING_MESSAGE_1, acc[0], signedMessage1.signedMessage), "Should verify message 1")
const signedMessage2 = await bitcoinWallet.signMessage(TESTING_MESSAGE_2, acc[0])
console.log("Signed message 2: ", signedMessage2)
assert(bitcoinMessage.verify(TESTING_MESSAGE_2, acc[0], signedMessage2.signedMessage), "Should verify message 2")
const signedMessage3 = await bitcoinWallet.signMessage(TESTING_MESSAGE_3, acc[0])
console.log("Signed message 3: ", signedMessage3)
assert(bitcoinMessage.verify(TESTING_MESSAGE_3, acc[0], signedMessage3.signedMessage), "Should verify message 3")
})
it("Get fees will return NaN", async () => {
it("Get fees, invalid argument", async () => {
try {
const acc = await bitcoinWallet.getAccounts()
const result = await bitcoinWallet.getFees(acc[0]);
} catch (err) {
assert.equal(err, "Cannot destructure property 'from' of 'transaction' as it is undefined.", "Should throw TyprError")
}
})
it("Get fees, valid argument", async () => {
const acc = await bitcoinWallet.getAccounts()
const { transactionFees } = await bitcoinWallet.getFee(acc[0]);
console.log("transactionFees ", transactionFees)
BTC_TXN_PARAM['from'] = acc[0]
const response = await bitcoinWallet.getFees(BTC_TXN_PARAM)
let actualResponse = Object.keys(response)
let expectedResponse = ['transactionSize', 'fees']
assert.deepEqual(actualResponse, expectedResponse, "Should have transactionSize and fees")
let satPerByte = Object.keys(response.fees);
let expected = [ 'slow', 'standard', 'fast' ]
assert.deepEqual(satPerByte, expected, "Should have slow, standard, fast fees")
})
it("Get fees with custom satPerByte", async () => {
it("Sign Transaction using estimated fee", async () => {
const acc = await bitcoinWallet.getAccounts()
const { transactionFees } = await bitcoinWallet.getFee(acc[0], 50);
console.log("transactionFees ", transactionFees)
BTC_TXN_PARAM['from'] = acc[0]
let response = await bitcoinWallet.getFees(BTC_TXN_PARAM)
BTC_TXN_PARAM['satPerByte'] = response.fees.slow.satPerByte
const { signedTransaction } = await bitcoinWallet.signTransaction(BTC_TXN_PARAM);
assert(signedTransaction)
})

@@ -99,5 +117,4 @@

const { signedTransaction } = await bitcoinWallet.signTransaction(BTC_TXN_PARAM);
console.log("signedTransaction ", signedTransaction)
} catch (err) {
console.log("Catching error ", err)
assert(err.message)
}

@@ -109,15 +126,12 @@ })

BTC_TXN_PARAM['from'] = acc[0]
BTC_TXN_PARAM['satPerByte'] = 60
BTC_TXN_PARAM['satPerByte'] = 15
const { signedTransaction } = await bitcoinWallet.signTransaction(BTC_TXN_PARAM);
console.log("signedTransaction ", signedTransaction)
assert(signedTransaction)
})
it("Should import correct account ", async () => {
if (opts.network === MAINNET) {
const address = await bitcoinWallet.importWallet(EXTERNAL_ACCOUNT_PRIVATE_KEY_MAINNET)
assert(address.toLowerCase() === EXTERNAL_ACCOUNT_ADDRESS_MAINNET.toLowerCase(), "Wrong address")
} else {
const address = await bitcoinWallet.importWallet(EXTERNAL_ACCOUNT_PRIVATE_KEY)
assert(address.toLowerCase() === EXTERNAL_ACCOUNT_ADDRESS.toLowerCase(), "Wrong address")
}
const address = await bitcoinWallet.importWallet(EXTERNAL_ACCOUNT_PRIVATE_KEY)
assert(address.toLowerCase() === EXTERNAL_ACCOUNT_ADDRESS.toLowerCase(), "Wrong address")
assert(bitcoinWallet.importedWallets.length === 1, "Should have 1 imported wallet")

@@ -128,7 +142,6 @@ })

const acc = await bitcoinWallet.getAccounts()
const balance = await getBalance(acc[0], opts.network)
console.log("acc ", acc)
console.log("balance ", balance)
const result = await getBalance(acc[0], opts.network)
assert(result.balance)
})
})

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc