diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 67b4e1f..b136db1 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -84,7 +84,8 @@ module.exports = { ['/guides/overview', 'Overview'], ['/guides/slp-implementation-instructions', 'Implementation Instructions'], ['/guides/create-slp-faucet', 'Create your own SLP Faucet'], - ['/guides/add-slp-icon', 'Add an icon for your SLP Token'], + ['/guides/add-slp-icon', 'Add an icon for your SLP Token'], + ['/guides/slp-software-setup', 'SLP software setup'], ] }, { diff --git a/docs/guides/overview.md b/docs/guides/overview.md index 26911f7..7b68506 100644 --- a/docs/guides/overview.md +++ b/docs/guides/overview.md @@ -11,3 +11,11 @@ Contains information on best practices and things to avoid if you are integratin By the end of this guide you will have a SLP faucet set up on your own web server. [View →](/guides/create-slp-faucet/) + +## SLP software setup + +By the end of this guide, you'll have the basic set of tools to be able to develop your own SLP project, and also your first programmatically created SLP token. + +[View →](/guides/slp-software-setup/) + + diff --git a/docs/guides/slp-software-setup.md b/docs/guides/slp-software-setup.md new file mode 100644 index 0000000..aa10336 --- /dev/null +++ b/docs/guides/slp-software-setup.md @@ -0,0 +1,370 @@ +# SLP software setup + +So, now that you’ve learned all the key theoretical concepts, we are set to start your journey as an SLP developer. + +To make sure you start with the right foot, let’s get you set up with the main tools you’ll need along the way. + +By the end of this guide, you'll have the basic software needed to be able to develop your own SLP project, and also your first programmatically created SLP token. + +### Install Git + +Official download - +[https://git-scm.com/book/en/v2/Getting-Started-Installing-Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) + +Remember that after installing Git, you need to add `C:\Program Files\Git\cmd` and `C:\Program Files\Git\bin` (your installation path) to the system path. +(For Windows: click `start menu` and type `Edit the system environment variable`) + +### Node.js and npm + +In order to continue, you’ll need to install both [Node.js](https://nodejs.org/en/) and [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). + +Done? Let’s continue. + + +### SLPJS + +For the SLP handling you’ll be using SLPJS. You can find a detailed guide on it [here](/packages/slpjs), but to keep things simple you should just run + +```bash +#initialize the environment setup +> npm init + +#install the required libraries +> npm install -g slpjs bitbox-sdk bitcore-lib-cash +``` + +Make sure everything is installed without errors. + + +## SLP wallet +Before moving on with the scripting, you’ll need some SLP addresses of your own to work with. If you already have some, feel free to skip these steps. + +First off, let’s get you an SLP compatible wallet. This will be the place where your freshly minted SLP tokens will reside, and also where you’ll have to send the required BCH to do so. After installing the software, we will make sure to keep the SLP address and it’s WIF. + +If you'd rather try things on testnet, you'll find a small comment after each section indicating the changes needed. To get testnet coins you can find several services, like the [bitcoin.com Testnet BCH faucet](https://developer.bitcoin.com/faucets/bch/) or the [Fullstack.cash Testnet BCH faucet](https://faucet.fullstack.cash/). + +### Option 1: Electron Cash SLP + +#### GUI + +[Check out our Electron Cash SLP edition walkthrough](/guides/slp-software-setup) **(Coming soon)** + +#### CLI + +**SLP wallet creation** + +```bash +> Electron-Cash-SLP-3.6.4.exe create_slp + +Password (hit return if you do not wish to encrypt your wallet): +Your wallet type is: slp_standard +Your wallet generation seed is: +"dinner valley cattle brush moment critic diagram party crack frost tilt shock" +Please keep it in a safe place; if you lose it, you will not be able to restore your wallet. +Wallet saved in 'c:\users\\appdata\roaming\electroncash\wallets\default_wallet' +``` + +**List addresses in wallet** + +```bash +> Electron-Cash-SLP-3.6.4.exe listaddresses + +[ + "qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh", + "qr70zmq9l7fyrnu4f9nz4fzp5a80zrfppc3s3w90lf", + "qqekzmw5tgxaqfps0f63wgjnx38q8jfv8cjlf8vzy5", + "qz6rgncgck5qa6ez8ej7t2yqc4yt9zmanyqxs5uxxf", + "qrlxewwqqsen2ude8alh6p50xd3tekdaqv28h3qsh7", + "qru0k6ws0dx44699xkvw7gmxpfavj9ll4sqkqm6vge", + ... +] +``` + + + +**Get the private key from an address** + +```bash +> Electron-Cash-SLP-3.6.4.exe getprivatekeys qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh + +WARNING: ALL your private keys are secret. +Exposing a single private key can compromise your entire wallet! +In particular, DO NOT use 'redeem private key' services proposed by third parties. +L1aeBNDeQ43PtFN2Jc9tbeqhNiqBgDHbxMctefHftHw4WtSo621e +``` + +As a last step, in order to send the needed BCH to the funding address, we need to make sure to use the Cash address format. For that conversion we will use the following call + +```bash +> Electron-Cash-SLP-3.6.4.exe addressconvert qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh + +{ + "cashaddr": "bitcoincash:qzvjfs5tuw56est4mwtzw3fegucgdwr00vrgaayqsf", + "legacy": "1ExkRUc6r128pvMkqwLGiLgfnmmdyYL6BA", + "simpleledger": "simpleledger:qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh" +} +``` + +After this, we will have generated the three necessary bits of information we need: + +**The WIF (private key)** +`L1aeBNDeQ43PtFN2Jc9tbeqhNiqBgDHbxMctefHftHw4WtSo621e` + +**The receiving SLP address** +`simpleledger:qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh` + +**And the funding cashaddr format address:** +`bitcoincash:qzvjfs5tuw56est4mwtzw3fegucgdwr00vrgaayqsf` + + +>**TESTNET** +>For the testnet version, just add the flag `--testnet` in each query + + + +### Option 2: bitcore + +Bitcore is a library used by SLPJS which, aside from many other things, allows us to create and manage new addresses using many different methods, like seed based, WIF based, or without any arguments at all. + +We can generate our new addresses using the following script. If you are not sure how to run it, use the same method as the balance.js and genesis.js described in the next section of the guide. + +### bitcoreAddr + +```javascript +const bitcore = require('bitcore-lib-cash'); + +var privateKey = new bitcore.PrivateKey(); + +var publicKey = new bitcore.PublicKey(privateKey) + +console.log("WIF:", privateKey.toWIF()); + +console.log("cashaddr:", publicKey.toAddress().toString()); +``` + +Result + +```bash +> npm run bitcoreAddr + +> @1.0.0 bitcoreAddr C:\Users\\ +> node bitcoreAddr.js + +WIF: Ky3QwBS8gKD2Cdu4iUe5aaqojsU8BfuT4Gf8zMT11AdsEkcN9UJj +cashaddr: bitcoincash:qp8x5ppmu4d0h2g2mgu405jkp5r4x87h7yya64tfd6 +``` + +This will give us: + +**The WIF (private key)** +`Ky3QwBS8gKD2Cdu4iUe5aaqojsU8BfuT4Gf8zMT11AdsEkcN9UJj` + +**And the funding cashaddr format address:** +`bitcoincash:qp8x5ppmu4d0h2g2mgu405jkp5r4x87h7yya64tfd6` + +Now to obtain the **receiving SLP address** we can use multiple different methods, like calling the Electron Cash CLI described in the previous section, or using any other resource that performs the conversion, like for example the [simpleledger.info explorer](https://simpleledger.info/) or the [bitcoin.com explorer](https://explorer.bitcoin.com/bch) which given a cashaddr format will display the corresponding simpleledger address for SLP. In this case: + +**Receiving SLP address** +`simpleledger:qp8x5ppmu4d0h2g2mgu405jkp5r4x87h7ygx3w7fny` + +>**TESTNET** +>For the testnet version, just add the following as the second line in the `bitcoreAddr.js` file +>```javascript +>bitcore.Networks.defaultNetwork = bitcore.Networks.testnet; +>```` + + +## Scripts + +Now that everything is set up, and we have all the information we need, we can take care of the code. + +Note that for these examples, we will be using the addresses we created using the Electron Cash SLP method. + +After running init, you should have a `package.json` file created in the same folder you have run the command. In this file you can find a section called `scripts`. There you can define various scripts you will be able to easily run with commands as follows + +```bash +> npm run +``` + +For this tutorial we want our scripts section to look like this. You can add more if you need them, for example if you're using the bitcore library to generate your addresses. + +```json +"scripts": { + "balance": "node balance.js", + "genesis": "node genesis.js" + }, +``` + +Now we only need to create the corresponding `genesis.js` and `balance.js` files on the same folder. Let’s get on that + +### balance.js + +```javascript +// Install BITBOX-SDK v8.1+ for blockchain access +// For more information visit: https://www.npmjs.com/package/bitbox-sdk +const BITBOXSDK = require('bitbox-sdk') +const slpjs = require('slpjs'); + +// FOR MAINNET UNCOMMENT +let addr = "simpleledger:qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh"; +const BITBOX = new BITBOXSDK.BITBOX({ restURL: 'https://rest.bitcoin.com/v2/' }); + +// FOR TESTNET UNCOMMENT +// let addr = "slptest:qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh"; +// const BITBOX = new BITBOXSDK.BITBOX({ restURL: 'https://trest.bitcoin.com/v2/' }); + +const bitboxNetwork = new slpjs.BitboxNetwork(BITBOX); + +let balances; +(async function() { + balances = await bitboxNetwork.getAllSlpBalancesAndUtxos(addr); + console.log("balances: ", balances); +})(); +``` + +Just switch the addr field to the address you want to check. + + +### genesis.js + +```javascript +// Install BITBOX-SDK v8.1+ for blockchain access +// For more information visit: https://www.npmjs.com/package/bitbox-sdk +const BITBOXSDK = require('bitbox-sdk') +const BigNumber = require('bignumber.js'); +const slpjs = require('slpjs'); + +// FOR MAINNET UNCOMMENT +const BITBOX = new BITBOXSDK.BITBOX({ restURL: 'https://rest.bitcoin.com/v2/' }); +const fundingAddress = "simpleledger:qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh"; // <-- must be simpleledger format +const fundingWif = "L1aeBNDeQ43PtFN2Jc9tbeqhNiqBgDHbxMctefHftHw4WtSo621e"; // <-- compressed WIF format +const tokenReceiverAddress = "simpleledger:qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh"; // <-- must be simpleledger format +const bchChangeReceiverAddress = "simpleledger:qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh"; // <-- cashAddr or slpAddr format +// For unlimited issuance provide a "batonReceiverAddress" +const batonReceiverAddress = "simpleledger:qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh"; + +// FOR TESTNET UNCOMMENT +// const BITBOX = new BITBOXSDK.BITBOX({ restURL: 'https://trest.bitcoin.com/v2/' }); +// const fundingAddress = "slptest:qpwyc9jnwckntlpuslg7ncmhe2n423304ueqcyw80l"; +// const fundingWif = "cVjzvdHGfQDtBEq7oddDRcpzpYuvNtPbWdi8tKQLcZae65G4zGgy"; +// const tokenReceiverAddress = "slptest:qpwyc9jnwckntlpuslg7ncmhe2n423304ueqcyw80l"; +// const bchChangeReceiverAddress = "slptest:qpwyc9jnwckntlpuslg7ncmhe2n423304ueqcyw80l"; +// // For unlimited issuance provide a "batonReceiverAddress" +// const batonReceiverAddress = "slptest:qpwyc9jnwckntlpuslg7ncmhe2n423304ueqcyw80l"; + +const bitboxNetwork = new slpjs.BitboxNetwork(BITBOX); + + +// 1) Select decimal precision for this new token +let decimals = 2; +let name = "My first awesome SLPJS Token"; +let ticker = "SLPJS"; +let documentUri = "info@simpleledger.io"; +let documentHash = null +let initialTokenQty = 1000000 + +let genesisTxid; + +// 2) Calculate the token quantity with decimal precision included +initialTokenQty = (new BigNumber(initialTokenQty)).times(10**decimals); + + +// 3) Get all balances at the funding address. +let balances; +(async function() { + balances = await bitboxNetwork.getAllSlpBalancesAndUtxos(fundingAddress); + console.log("'balances' variable is set."); + console.log('BCH balance:', balances.satoshis_available_bch); + +// 4) Set private keys + balances.nonSlpUtxos.forEach(txo => txo.wif = fundingWif) + +// 5) Use "simpleTokenGenesis()" helper method + genesisTxid = await bitboxNetwork.simpleTokenGenesis( + name, + ticker, + initialTokenQty, + documentUri, + documentHash, + decimals, + tokenReceiverAddress, + batonReceiverAddress, + bchChangeReceiverAddress, + balances.nonSlpUtxos + ) + console.log("GENESIS txn complete:",genesisTxid) +})(); +``` + +Here you’ll want to make sure to change the `fundingAddress`, `fundingWif`, `tokenReceiverAddress`, `bchChangeReceiverAddress` and `batonReceiverAddress` in the script to the ones you obtained in the previous steps. Also, remember to send a small amount of BCH to the funding address. Keep in mind that if you choose to send the funds using a non-SLP compatible wallet, you'll need to use the cashAddr format address. Some cents will be enough. + +Also, here you can edit the parameters of the new token. Feel free to change the `decimals`, `name`, `ticker`, `documentUri`, `documentHash` and `initialTokenQty` to match the desired specifications of your token. Or just leave the defaults for testing purposes. + + +>**TESTNET** +>For the testnet version, both scripts include `MAINNET` and `TESTNET` blocks you can comment/uncomment + +## Run + +Now we are ready to finally test our scripts. Lets begin with balance + +```bash +> npm run balance +``` + +This will return all the balances and UTXOs associated with the address provided in the script + +```js +balances: { + satoshis_available_bch: 86546, + satoshis_in_slp_baton: 0, + satoshis_in_slp_token: 0, + satoshis_in_invalid_token_dag: 0, + satoshis_in_invalid_baton_dag: 0, + satoshis_in_unknown_token_type: 0, + slpTokenBalances: {}, + nftParentChildBalances: {}, + slpTokenUtxos: {}, + slpBatonUtxos: {}, + nonSlpUtxos: [ + { + satoshis: 86546, + txid: '6f51b4d203c3998c5012cebfd375f69407d3e52a5468540efdc7ab99db8955cc', + amount: 0.00086546, + confirmations: 0, + height: undefined, + vout: 0, + cashAddress: 'bitcoincash:qzvjfs5tuw56est4mwtzw3fegucgdwr00vrgaayqsf', + legacyAddress: '1ExkRUc6r128pvMkqwLGiLgfnmmdyYL6BA', + slpAddress: 'simpleledger:qzvjfs5tuw56est4mwtzw3fegucgdwr00v0nkx3qwh', + scriptPubKey: '76a9149924c28be3a9acc175db96274539473086b86f7b88ac', + tx: [Object], + slpUtxoJudgement: 'NOT_SLP' + } + ], + invalidTokenUtxos: [], + invalidBatonUtxos: [], + unknownTokenTypeUtxos: [] +} +``` + +The most relevant bit of information at the moment is the `satoshis_available_bch: 86546` where you can confirm your address is properly funded. + +Now let’s go ahead and run genesis + +```bash +> npm run genesis +``` +Which returns + +```js +'balances' variable is set. +BCH balance: 86546 +GENESIS txn complete: a17054f4cdb99fca43ad8ae218fd55c53814c02c450fb540a263edab5f1ac527 +``` + +Try checking the transaction on your favourite SLP explorer, like [simpleledger.info](https://simpleledger.info/), and you’ll find the info on your newly minted token. Plus the tokens will be waiting for you in your SLP wallet. + +Now that you have completed your first steps in the SLP world, you can start imagining what your future project will look like. + +**Our next tutorials are right around the corner. Check back soon!** diff --git a/package-lock.json b/package-lock.json index 538e6fb..f46188a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3624,9 +3624,9 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, "eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" }, "events": { "version": "3.1.0", @@ -4003,22 +4003,9 @@ } }, "follow-redirects": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz", - "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==", - "requires": { - "debug": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - } - } + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", + "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==" }, "for-in": { "version": "1.0.2", @@ -4996,14 +4983,14 @@ } }, "http-parser-js": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", - "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=" + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", + "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==" }, "http-proxy": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", - "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "requires": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -9798,19 +9785,19 @@ } }, "websocket-driver": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", - "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "requires": { - "http-parser-js": ">=0.4.0 <0.4.11", + "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" }, "whatwg-url": { "version": "7.1.0",