A Step-by-Step Guide to Developing Bitcoin Smart Contracts

We show the complete workflow of the design, development, testing, deployment, and invocation process of smart contracts in sCrypt.


The first step in building any smart contract is a high-level design. Here we choose to implement a common transaction type Pay To Public Key Hash (P2PKH) in the Bitcoin network. There are two main reasons to use this type of transaction as an example:

What’s P2PKH?

It’s locking script is:


It’s unlocking script is:

<Signature> <Public Key>

Receiving of P2PKH

If Alice wants to transfer bitcoins to Bob, Bob first needs to tell Alice his public key hash value ( usually known as the bitcoin address, which is similar to a bank account number). Alice uses this value to construct a P2PKH locking script (here denoted as LS¹) and sends the transaction to the miner. After the miner verifies that it is correct, the transaction is recorded on chain.

Spending of P2PKH

When Bob wants to spend the bitcoins received, he needs to provide two pieces of information to construct a unlocking script:

After constructing the unlocking script and placing it in a transaction, Bob broadcasts the transaction.

Verification of P2PKH

When miners receive the new transaction, they need to validate it, which involves two main steps:

<Signature> <Public Key> OP_DUP OP_HASH160 <Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG

If the verification passes, it proves that Alice does own and can control the bitcoins, and the miner will record the new spending transaction on chain. This is the main process and principle of P2PKH type transactions.

In summary, our goal for contract design is also very clear: to implement a sCrypt contract that is equivalent to the function of P2PKH.


With this design goal in mind, we can get started. Firstly, we install the sCrypt extension in VS Code.

A sample project is provided for anyone to quickly learn how to develop and test sCrypt contracts. It is a good place to start. First clone the project locally, using the command:

git clone git@github.com:sCrypt-Inc/boilerplate.git

Actually, the P2PKH contract we wanted is included in the project. So let us look directly at the code at contracts/p2pkh.scrypt.

Contract DemoP2PKH

The main body of the contract includes:

It is obvious that the sCrypt implementation is much easier to learn and write than previous scripts. And the more complex the contractual logic, the more obvious the advantage of using sCrypt is.

Unit testing

With the code, the next step is to verify that the implementation is correct, and the normal way to do this is to add some unit tests. The test file for the above contract is tests/js/p2pkh.scrypttest.js, the code is as follows:

Anyone familiar with JavaScript might immediately recognize that this is a pure JS test file based on the MOCHA + Chai framework. Let’s take a closer look at this test case.

const { buildContractClass, bsv } = require('scrypttest');
const DemoP2PKH = buildContractClass(path.join(__dirname, '../../contracts/p2pkh.scrypt'), tx, inputIndex, inputSatoshis)
demo = new DemoP2PKH(toHex(pkh))
sig = signTx(tx, privateKey, demo.getLockingScript())
expect(demo.unlock(toHex(sig), toHex(publicKey))).to.equal(true);
sig = signTx(tx, privateKey2, demo.getLockingScript())
expect(demo.unlock(toHex(sig), toHex(publicKey))).to.equal(false);

Before running the test, we need to run npm install in the root directory of the project to ensure that the dependencies have been successfully installed. Then right click the test file in VS Code editor and select “Run sCrypt Test” ; The result is shown in the “Output” view.


Having only the above unit test is not enough, because when the test fails, we can hardly figure out why and there is no more information to help us pinpoint the problem. Enter the sCrypt Debugger.

A sample debug configuration for the DemoP2PKH contract can be found in the file .vscode/launch.json :


The key parameters are:

So how does one obtain these arguments in general? Looking back at the previous test file, you can see that there are several log statements commented out:

This log is exactly what you need for the debug configuration, and you can get it for other contracts in a similar way.

With the configuration in place, you can use the “F5” shortcut to start code debugging. See the previous article and the official VS Code documentation for details of what the debugger does and how to use it.

Deploy and Call the Contract

Before using a contract in a production environment, a developer should test on testnet to ensure that the contract code meets expectations. For the example in this article, you can run the command node tests/js/p2pkh.js in the project root directory.


When we first run the file, we see output like this:

New privKey generated for testnet: cMtFUvwk43MwBoWs15fU15jWmQEk27yJJjEkWotmPjHHRuXU9qGq
With address: moJnB7AND5TW8suRmdHPbY6knpfE1uJ15n
You could fund the address on testnet & use the privKey to complete the test

Because the code requires two things to work:

If you already have such a private key, you can find and modify the following line of code (using a private key in WIF format instead of a empty character) :

const privKey = ''

Of course, you can also use the private key in the output above, but first you need to get the test coin for the address in the output, such as on this site.

Expected Result

With the aforementioned setup in place, you are ready to run the command again. Normally you would see the following output:

Contract Deployed Successfully! TxId: bc929f1dddc6652896c7c162314e2651fbcd26495bd1ccf9568219e22fea2fb8
Contract Method Called Successfully! TxId: ce2dba497065d33c1e07bf710ad94e9600c6413e053b4abec2bd8562aea3dc20

The above result shows that the contract deployment and call have been successful, and you can go to a blockchain browser to see the corresponding transaction details (using the txid in the output results) .

The full code is available atdeployments/p2pkh.js.


Let’s take a look at the specific implementation of contract deployment and invocation.

Contract Deployment

const amountInContract = 10000
const deployTx = await createLockingTx(privateKey.toAddress(), amountInContract)

2. Set the script for the output to be the locking script above:


3. Transaction Signature:


4. Broadcast transaction to the bitcoin network:

const deployTxId = await sendTx(deployTx)

Contract Call

const spendAmount = amountInContract / 10
const methodCallTx = createUnlockingTx(deployTxId, amountInContract, p2pkh.lockingScript.toASM(), spendAmount)

2. Get a signature for this transaction:

const sig = signTx(methodCallTx, privateKey, p2pkh.lockingScript.toASM(), amountInContract)

3. Get the unlocking script corresponding to the contract method call.

const unlockingScript = p2pkh.unlock(new Sig(toHex(sig)), new PubKey(toHex(publicKey))).toScript()

4. Set the corresponding input script to the above unlocking script:


5. Send transaction to the network:

const methodCallTxId = await sendTx(methodCallTx)


We walk through an example of developing a smart contract on Bitcoin, which serves a starting point for developing more.

Translated from article by Yiqiang Wang with help of Hongfeng Zheng.

sCrypt Inc (https://scrypt.io) is a company with a mission to provide integrated on-chain smart contracting solutions using the original BitCoin Protocol on BSV