Building a wallet for your SLP-based application can be easy or difficult depending on the approach taken and what the goals are. How a wallet is implemented can have profound impacts on user convenience and security. Building a secure wallet for SLP can be difficult, so in this post we have provided a list of important features that all slp wallets should have. Before we dive into tips for how to build a secure SLP wallet, lets take a step back and understand the reasons why building wallets has become popular.
Why Build an SLP Token Wallet
Any application that interacts with cryptocurrency or crypto tokens, needs to have an integrated wallet or some way to communicate with a wallet. As more use cases for cryptocurrencies emerge, more wallets need to be built, improved, or extended to be compatible with those specific use cases. As you may be aware, the cryptocurrency ecosystem is in the early phases of what some have referred to as a “Cambrian Explosion” for tokens, as Stefan Rust stated in this recent interview.
“We’re going to see many different types of tokens emerge that are going to tie communities together from governance tokens, to social tokens, utility tokens, security tokens, etc.”
At the moment the three primary applications driving crypto token adoption appear to be (1) DeFi, (2) NFTs (non-fungible tokens), and (3) stable coins and CBDC (central bank digital currencies). Perhaps NFT applications have experienced the most recent hype as we wrote about recently, but all of these applications are experiencing high growth. It’s important for us to point out that the entire ecosystem is still in early phases, and experimental in nature which carries inherent risks.
An easy way to get a feel for the kind of activity going on within the SLP ecosystem is to peruse the SimpleLedger.info block explorer. It seems most activity is driven by enthusiastic experimentation by individuals and businesses. It’s pretty exciting to see so much activity, and we are even more excited about what the future holds for the SLP tokens ecosystem. Possible new features, such as Group Tokenization and Transaction Introspection, would dramatically expand the set of possible applications through the use of the Bitcoin Cash script language. These upgrades would make tokens more convenient and secure for Bitcoin Cash users and simplify token integration for non-Bitcoin application developers. We will report more on these developments later as community discussions progress on various platforms, including The Bitcoin Cash Network Discussions. Now, let’s start exploring tips for how to build a secure SLP tokens wallet.
How to Build a Secure SLP Token Wallet
For our shining example of a secure SLP wallet, we are going to use the free and open source Electron Cash SLP Edition (ECSLP) wallet, which is not perfect, but great attention has been put into making it the most versatile SLP wallet without any compromise on security. The ECSLP wallet is special because it was the first SLP wallet built and no special SLP servers are required to send tokens. It works by connecting with a standard Electron Cash protocol server which knows nothing about SLP. It’s possible this is why SLP became popular in the first place. That is, no special SLP server infrastructure is required to use or validate tokens. As we will describe later in security considerations, the reliance on special SLP servers has started to evolve with other wallet implementations as SLP popularity has increased. This can pose security vulnerabilities for the users if wallets don’t implement SLP with care.
Let’s consider the logical flow of SLP tokens validation in the initial version of ECSLP released in 2018. The SLP tokens protocol v1 is light-wallet compatible because the wallet is subscribed to a set of addresses with a server and will receive a notification whenever a possible SLP output (BCH dust) is received, but the SPV verification process is not able to say whether or not the token is valid. SLP validity judgement of a token requires a second validation process that runs after or in parallel with the wallet SPV process. The following logic diagram shows the logic diagram of how ECSLP validates tokens.
Important Elements Added to ECSLP
Since the initial fork of Electron Cash mainline in 2018 some important features have been added for the purpose of usability and security. We’ve listed the most critical elements below along with our rationale for adding each feature. Most of these recommendations are already documented on the slp.dev website in the wallet implementation guidance document, but it never hurts to review and improve.
- Using SLP Address Format. Sending tokens to non-SLP wallets will cause tokens to be burned because non-SLP wallets would treat the received coins as regular Bitcoin Cash (spending token coin(s) without coloring them with an OP_RETURN metadata message). To help prevent this from happening the SLP ecosystem uses a special address format called SlpAddr format, a modified version of the CashAddr format standard. The SlpAddr address prefix is “simpleledger:”, instead of “bitcoincash:”, and SLP wallets should format their receiving addresses to use this format so other wallets would know the receiving wallet is SLP compatible. (Special Note: This doesn’t guarantee the receiver has an SLP wallet because the sender or receiver could still unwittingly use a converter tool, but this significantly reduces the problem. Wallets also should take care to help inform users about the risks of using converter tools, and ECSLP provides an example of how to accomplish this in the Address Converter tab.)
- Using a Separate HD Derivation Path for SLP. Non-SLP wallets can unknowingly burn SLP tokens because there is no consensus rules to prevent token burning. To help alleviate this problem SLP has been registered as coin type 245 in the the BIP44 registry and ECSLP uses this path as the default for all newly created wallets. If this sounds like French to you, let us quickly explain. Most wallets generate and provide users with a secret 12-word mnemonic seed phrase which maps to the user’s master private key, normally following the BIP39 standard. Many wallets also conform to the BIP44 standard for deriving individual private keys from the master private key, which defines a different derivation for each cryptocurrency (Bitcoin Cash is coin type 145). All SLP token wallets should utilize BIP44 with the 245 coin type as the default derivation path (i.e., `m/44’/254’/0’/<isChange>/<index>`). This will help prevent a user’s tokens from being burned if they decide to use same seed-phrase in a non-SLP compatible wallet. Using dual paths to separate BCH from SLP coins is an option, but implementing this adds complexity without much benefit.
- Pre-Broadcast Checker. The additional complexity of constructing an SLP transaction can lead to accidental token burns if constructed incorrectly. The lack of any token consensus rules allows poorly implemented wallets to burn tokens. This is normally caused by incorrectly formatting an SLP metadata message in OP_RETURN, rounding errors caused by programming languages without big number types, or another error causing an imbalance between valid input quantity and output quantity indicated in SLP metadata. To help catch errant SLP transactions before signing and broadcasting, wallets should implement a static method that deserializes a raw transaction to double-check the transaction satisfies the user’s requested outputs. If well implemented, this type of check will prevent token burns caused by regressions in the wallet’s transaction construction logic.
- Disable BIP-L101 Output Sorting. Electron Cash implemented BIP-L101 output sorting in order to help wallets achieve better privacy, but this can inadvertently cause SLP outputs to be reordered in a way leading to burned tokens. Any kind of output sorting needs to be disabled with SLP transactions, which may not be obvious since such sorting algorithms can be buried in code apart from the initial transaction construction.
- Graph Search. Light wallets using local validators are slower because the wallet doesn’t know anything about token’s previous transaction history if the wallet isn’t involved in the transactions. This means when a wallet receives a potential SLP transaction it must look at the transaction inputs to crawl back through the transaction history until reaching the token’s Genesis or some previously validated transaction. This is a blind process of crawling back because the parent’s transaction inputs are unknown until the wallet downloads the parent transaction and analyzes its inputs. As you can imagine this process can take an immense amount of time caused by the network latency of making many network calls to a server, the server’s retrieval time, and the actual data download time. To help improve validation times we built a custom server that can send the wallet all of the needed transactions in a single network call, we call this Graph Search. The wallet asks the server for a sub-graph required to validate, including any transaction we know are valid, and the server sends back the batch of transactions required for validation. If for some reason the Graph Search server makes a mistake or times out then the wallet falls back to the original blind validation.
- Pre-Flight Validation Checker. Even though the SLP validation rules are extremely simple and cannot be changed, reliance on a single validator implementation carries some risk due to the possibility of false positive validation. A false positive validation can cause a wallet to think an input is valid, when it is actually invalid, leading the wallet to include it in an SLP transaction. The BCH blockchain would accept the transaction, but SLP validators would judge the transaction as invalid, making the tokens inputs burned. Even though ECSLP performs local validation the wallet also leverages the CheckSlpValidity endpoint that is available in the new BCHD indexer for SLP. To help prevent token burns caused by false positive validations, all wallets should perform a pre-flight validity check, before signing, with a different self-hosted validator implementation.
- Don’t Spend Invalid Tokens Unless the User Verifies. False negative validation judgments could also lead to inadvertent token burns. This would happen if the validator believes a valid token is invalid, and then the wallet treats the coin as vanilla Bitcoin Cash. For this reason, wallets should never spend SLP tokens that have been judged as invalid without allowing manual verification from the user. If the user reviews and verifies that the purportedly invalid token as actually invalid, then the user should be allowed to unlock and spend the coin as Bitcoin Cash. At a bare minimum, the wallet should not allow spending of potential SLP coins that have been judged as invalid. This means any transaction output in a transaction that contains the SLP magic bytes 0x00504c53 encoded with little-endian byte ordering within the first output script should protect dust outputs from being spent. Refer to the Bitcoin Cash specification OP_RETURN guidelines for more details, the magic is also referred to as a “Lokad Prefix”. (Fun fact: the magic lokad bytes contain “SLP” when using UTF-8 encoding.)
Standard SLP Unit Tests and Fuzzing
As described in the implementation guidance, there are two distinct sets of unit tests maintained in the unit-test repo on GitHub that need to pass without fail. These tests help the wallet application implementer enforce (1) proper parsing and formatting for SLP OP_RETURN metadata messages, and (2) input validation scenarios for the GENESIS, SEND, and MINT transaction types. In addition to the unit tests, all new implementations should utilize differential fuzzing in order to catch any language or implementation specific bugs in a SLP message parser outside the scope of the unit tests. Any bugs found during a fuzzing campaign which you think should be added to the unit tests can considered through a pull request. Currently there are some existing fuzzer implementations available, there’s one written in c++ and another written in golang.
We encourage everyone to utilize existing libraries available to reduce development burden, but all wallet and application developers need to self-verify that the aforementioned tests are passing for their wallet regardless if they’re using a library or home-rolled methods.
Third-party Hosted Validators Can Burn Tokens
We advise all wallets to avoid sole reliance on third-party hosted validators because those validators can return false positive validation responses which can cause a loss of funds. Even if a pre-flight validation check is performed using two different third-party validators, the different parties could coordinate false positive responses in an attack. A false positive validation can cause a wallet to think an input is valid, when it is actually invalid, leading the wallet to include it in an SLP transaction. The BCH blockchain would accept the transaction, but SLP validators would judge the transaction as invalid, and the tokens are burned.
SLP wallet developers and application builders using the SLPv1 protocol need to take extra precautions when handling user’s tokens because SLP tokens are not validated by PoW miners. This is an additional inconvenience to all SLP developers and application users, and we are confident that this will be improved in subsequent versions of SLP coming soon. Please stay tuned!