Satchel is a minimal Bitcoin SV headless HD wallet for the web. It it is designed to speed up development of new Bitcoin apps without enforcing any UI opinions. It is a collection of convenience functions that work together to perform common wallet actions like importing private keys, making transactions, cleaning up UTXOs, and monitoring Bitcoin network activity. It uses bitsocket to monitor the logged in address tree, and triggers a callback to your application when related activity is seen on the network. It does not require you to run a bitcoin node or any other software. It relies on a few external services to keep the size as low as possible.
- Installation
- Documentation
- Examples
- Code Standards
- Usage
- Maintainers
- Contributing
- Dependencies
- License
You need npm or yarn installed.
On a mac you can use homebrew to install the above prerequisites
Clone the repo, cd into the folder and yarn
(npm install
)
$ git clone https://github.com/rohenaz/satchel.git
$ cd satchel
$ yarn
To generate a new satchel.min.js file, run the yarn build
command (npm run build
)
$ yarn build
To launch the example via a local-web-server
Then open your browser to http://localhost:8000/example/index.html
$ yarn serve
Below are all the options available to configure satchel.
option | description | required | type | default |
---|---|---|---|---|
bitIndexApiKey | Grab this from BitIndex | ✔️ | string | 1DGD3... |
planariaApiKey | Grab this from BitDB | ✔️ | string | 1XGGY... |
planariaUrl | Modify this if you are running a custom Planaria. | string | https://genesis.bitdb.network/q/1Fna... | |
bitsocketUrl | Modify this if you are running custom bitsocket instance. | string | https://chronos.bitdb.network/s/1P6o.. | |
feePerKb | Satoshis per kilobyte for fee. | integer | 1000 | |
rpc | What rpc service to use for sending transactions. | string | https://api.bitindex.network | |
maxUtxos | The maximum number of utxos to return when calling satchel.utxos(). Will return utxos with highest value. | integer | 5 | |
txsQuery | Data to query Planaria with when getHistory is called. | function | () => txsQuery() |
|
bitsocketListener | This creates a bitsocket on login and closes on delete. Used for watching new transactions. Set to null if you don't want it to run. |
function | () => {} -> EventSource (see code) |
All the power from included libraries is at your finger tips:
moneybutton/bsv
is available at satchel.bsv
.
moneybutton/mnemonic
is available at satchel.Mnemonic
Retrieves the Address object associated with logged in user.
satchel.address()
Retrieves the string representation of the logged in address. This could be used to look up on an explorer website.
satchel.address().toString() === '1....'
Retrieves the amount of satoshis that are confirmed and unconfirmed combined.
if (satchel.balance() > 100000000) {
console.log('you have at least 1 Bitcoin')
}
Retrieves the amount of satoshis that are confirmed for the account.
if (satchel.confirmedBalance() === 0) {
console.log('you have no confirmed Bitcoin')
}
Retrieves the amount of satoshis that are unconfirmed for the account.
if (satchel.unconfirmedBalance() === 0) {
console.log('you have no unconfirmed Bitcoin')
}
Retrieves the utxo set associated with an address. This is used for sending transactions. By default all utxos are used as inputs, up to a maximum of 5 to prevent very large transactions that may fail to broadcast on wallets with a high number of utxos. You may provide an optional maximum number of utxos to consume. Passing null will use all of them regardless of tx size.
for (const utxo of satchel.utxos()) {
console.log(utxo['txid'])
}
Retrieves the individual private key of the current address. For an extended key, use satchel.hdPrivateKey() instead.
if (satchel.privateKey().publicKey.compressed) {
console.log('your public key is compressed')
}
Checks if currently logged in.
if (! satchel.isLoggedIn()) {
console.log('not logged in')
}
Performs a basic transaction: to send N satoshis to an address.
const address = satchel.bsv.Address.fromString('1...')
const sats = 2000
let tx = await satchel.send(address, sats)
console.log('transaction sent')
console.log(tx)
Removes all outputs with more than 0 and less than 546 satoshis. This is a protocol limit.
let tx = new satchel.bsv.Transaction()
tx.from(satchel.utxos())
tx = satchel.cleanTxDust(tx)
Adds one or more OP_RETURN
data points.
To use this pass an array in datapay format.
let tx = new satchel.bsv.Transaction()
tx.from(satchel.utxos())
tx = satchel.addOpReturnData(tx, ['0x6d01', 'testing testing'])
Sends a transaction off to the network. This uses the satchel.rpc
option to choose a server. It sends the serialized form of a transaction to a bitcoin node. A callback may be provided in order to perform additional processing after the broadcast has completed. send
uses this internally to actually broadcast the transaction. The safe
parameter is used to choose between safe serialization or just conversion to string. In case of using OP_RETURN you must disable safe mode, and therefore bitcore-lib-cash will not give an error on broadcast.
const address = satchel.bsv.Address.fromString('1....')
const sats = 2000
let tx = new satchel.bsv.Transaction()
tx.from(satchel.utxos())
tx.to(address, sats)
tx.feePerKb(satchel.feePerKb)
tx.change(satchel.address())
tx = satchel.cleanTxDust(tx)
// TODO - show lookup private key example instead
tx.sign(satchel.privateKey())
let response = await satchel.broadcastTx(tx)
console.log('transaction broadcast')
console.log(tx)
Retrieves the logged in addresses balance and updates localStorage
, these values are set:
satchel-wallet.confirmed-balance
satchel-wallet.unconfirmed-balance
let balance = await satchel.updateBalance()
console.log('new balance is ', balance)
Retrieves the utxo set for the logged in address. The callback contains the json response.
let data = await satchel.updateUtxos()
console.log('you have ${satchel.utxos().length} utxos', data)
Retrieves transaction history across address tree.
let response = await satchel.getHistory()
console.log('history retrieved', response)
Creates a new HD wallet and logs in with it. Returns the new mnemonic passphrase.
let mnemonic = await satchel.new()
console.log('wallet created', mnemonic)
Creates a new bsv.Transaction object from datapay formatted array and signs it with the current child private key. Returns the Transaction object. Address and satoshis are optional inputs for creating a second output sending some BSV to the provided address.
let tx = await satchel.newDataTx(['yourdata', 'goes', 'here', '0x123'])
console.log('Tx created and ready to broadcast:', tx.toString())
Gets the next unused address information from BitIndex. This includes the chain, num, and address. Sets satchel.num
key in localStorage.
let nextAddressObj = await satchel.next()
console.log('Next unused address:', nextAddressObj.address)
Takes an HTMLAnchorElement and sets the href and download attributes to turn it into a 'download mnemonic' link. When clicked, a .txt file is downloaded containing your mnemonic passphrase. It will also remove the 'style:none' css attribute, making the button visible only when a mnemonic is available to download.
<a id="downloadLink" style="display:none;">Download Mnemonic</a>
let el = document.getElementById('downloadLink')
let nextAddressObj = await satchel.setMnemonicAnchor(el)
console.log('Now you can click the download link', nextAddressObj)
Performs a query on the bitdb database which results in a JSON object. Read more here about bitdb.
const testQuery = {
'v': 3,
'q': {
'find': {
'in.e.a': satchel.address().toString()
},
'limit': 10
},
'r': {
'f': '[ .[] | { block: .blk.i?, timestamp: .blk.t?, content: .out[1]?.s2 }]'
}
}
let r
try {
r = await satchel.queryPlanaria(testQuery)
console.log(r)
} catch (e) {
console.error("Failed to query Planaria", err)
}
Logs in with extended private key string. You will typically not need to call this yourself.
const xprv = 'xprv...';
await satchel.login(xprv)
// do some html stuff or something here, will run after localStorage is updated.
console.log('logged in')
Logs out. With normal operation you will not need to call this yourself. This is called when the logout button is clicked.
satchel.logout()
console.log('logged out')
Gets the bsv value of some satoshis like 13370000. Use this because Javascript's number handling will introduce small errors otherwise.
let bitcoin = satchel.sat2bsv(10000)
console.log('converted', bitcoin)
Gets the satoshis of a bsv amount like 0.1337. Use this because Javascript's number handling will introduce small errors otherwise.
let satoshis = satchel.bsv2sat(1.22)
console.log('converted', satoshis)
Generates link href for a Whats On-Chain url address.
console.log(satchel.receiveAddressLink('1...'))
// logs out https://whatsonchain/address/1...
Generates link href for a Whats On-Chain tx.
console.log(satchel.txLink('tx...'))
// logs out https://whatsonchain/tx/tx...
Returns a url to a qr-code for the current address.
console.log(satchel.qrCode(300, 'svg'))
// logs out https://api.qrserver.com/v1/create-qr-code/?data=16cbuoPEy2LkfacZYA47wA1CvThRZTjzCX&size=300x300&format=svg
View the working example code, or run the example locally
- map.sv uses satchel for generating and broadcasting transactions
- DTV uses satchel for their visitors wallet solution
Always use the language's best practices!
There are examples above using satchel in the wild.
- Add satchel to your page
<script src="/node_modules/bsv-satchel/dist/satchel.min.js">
- Initialize satchel to get started
satchel.init({
'bitIndexApiKey': 'BITINDEX_API_HERE',
'planariaApiKey': 'PLANARIA_API_HERE',
'feePerKb': 1337
})
Note: the satchel library will be available from window.satchel
Use as an npm package
$ yarn add bsv-satchel
Support the development of this project and the Satchel team 🙏
Feel free to dive in! Open an issue or submit PRs.
Satchel is powered by several 3rd-party services and public npm packages.
- Chronos (socket)
- Genesis (tx history)
- Planaria (planaria)
- BitIndex (xpub monitor)
- QR Server (qr codes)
- bsv (bsv library)
- satoshi-bitcoin (conversions)
- qrcode-svg (qr codes)
- local-web-server (dev)