This article explains a simple and extensible way to build fungible tokens on Bitcoin. We cover how to write a token smart contract in Javascript and how to mint and send tokens.
We use the Bitcoin Computer, a smart contract system for Bitcoin and Litecoin (available on Npm and Github).
The Smart Contract
A smart contract for the Bitcoin Computer is a Javascript ES6 class.
class Token {
constructor(supply, name, pubKey) {
this.coins = supply
this.name = name
this._owners = [pubKey]
} send(amount, to) {
if (this.coins < amount) throw new Error()
this.coins -= amount
return new Token(amount, this.name, to)
}
}
The constructor of the Token
class creates an object that stores the supply and name of the token. The property _owners
is a keyword in smart contracts and setting it guarantees that only the holder of the private key corresponding topubKey
can send tokens initially.
The send function checks whether the amount of tokens in the current object is sufficient to cover the number of tokens to be sent. It throws an error if insufficient funds are detected. Otherwise, it reduces the amount of tokens in the current object by number. The method returns a new Token
object with amount
-many tokens that are owned by the recipient to
.
Example
The image shows the transactions that are broadcast when a token is created and transferred between users. Enclosing boxes represent transactions, colored boxes represent outputs, arrows represent spending relations. The numbers indicate the number of tokens in an output and the color represents the owner of the tokens.
Note that the transactions mimic the way that Bitcoin transfers satoshi between users. Sending tokens this way costs no more than sending Bitcoin.
Minting a Token
Minting a token means broadcasting a transaction that records the creation of the token. To broadcast the transaction we create a wallet by instantiating the Bitcoin Computer library.
import Computer from 'bitcoin-computer'const seed = <some BIP39 seed>
const computer = new Computer({ seed }))
The object computer
that is returned can deploy Javascript objects to the Bitcoin blockchain via its new
method. The creation of the new object is recorded in a transaction.
import Token from './token'const to = '03223d...46d06c8dfe'
const supply = 1000000
const name = 'Bitcoin Token'
const token = await computer.new(Token, [supply, name, to])
The computer.new
method returns an object token
that has all the properties and methods of the Token
class. We call token
a smart object because its state is backed up on the blockchain. Each smart object has a property _id
that remains fixed throughout its lifecycle. It encodes the output where the object was deployed.
Sending a Token
The send method creates a new smart object sentToken
with the given amount of tokens. A function call on a smart object is recorded in a transaction so we have to await
on the function call to sign and send it.
const amount = 50000
const to = '03223d...46d06c8dfe'
const sentToken = await token.send(amount, to)
The sentToken
object is also a smart object owned by to
. In addition to the _id
property, every smart object also has a property _rev
that encodes the output where its latest state is recorded. This _rev
can be sent to the recipient of the token transfer through email, a website, or any other means.
Receiving a Token
The computer.sync
method returns the smart object stored at a given output. Receiver can pass the _rev
obtained from Sender into computer.sync
to compute the state of the object stored at that location.
const id = 'd2242c...43ffb:0'
const receivedToken = await computer.sync(id)
// expect.toEqual(receivedToken, sentToken)
The object receivedToken
has the same state as sentToken
but is stored on Receiver’s machine. Receiver can inspect the state to verify that receivedToken
contains the expected number of tokens. However, Receiver also needs to check that Sender has sent the correct token. After all, an attacker can deploy a counterfeit token with the same type and state. How can Receiver distinguish the official token A from a counterfeit token B?
This problem can be solved by inspecting the _root
property of the smart object. The _root
property of a smart object is always set to the root of the dependency tree that is built during the evolution of a smart object.
All three properties _id
, _rev
and _root
encode outputs. Note that the counterfeit token has a different root output than the original token. All Receiver needs to do is to check if the _root
is as expected. If so Receiver can safely accept the token.
Demo
If you want to try it out for yourself you can download the code from Github. The app is built to be a starting point for new token projects and to be easy to integrate into existing Javascript wallets.
Build Your Own Token
You can launch your own token today. No previous knowledge other than Javascript is required. There is more information in the docs and if you have any questions feel free to join our Telegram group.