Skip to content

Commit dd5c4ef

Browse files
authored
Merge pull request #283 from sCrypt-Inc/btc_docs
add btc docs
2 parents fe2f92c + bb59c64 commit dd5c4ef

File tree

190 files changed

+11872
-32
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

190 files changed

+11872
-32
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"position": 3,
3+
"label": "Bitcoin Basics",
4+
"collapsible": true,
5+
"collapsed": true
6+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
sidebar_position: 1
3+
---
4+
# Bitcoin Basics
5+
6+
If you are new to Bitcoin development, we recommend exploring the resources listed in this section. While not a required prerequisite, going over these guides will help you develop a better understanding of key concepts and make it easier to begin developing sCrypt smart contracts.
7+
8+
If you are already familiar with Bitcoin, feel free to skip ahead to the [Basics](../how-to-write-a-contract/basics.md) section.
9+
10+
## Resources
11+
12+
If you want to learn more about the fundamentals of Bitcoin, consider exploring these helpful resources:
13+
14+
15+
- [Learn me a Bitcoin](https://learnmeabitcoin.com/)
16+
- [sCrypt Academy](https://academy.scrypt.io/)
17+
- [BTC Wiki](https://en.bitcoin.it/wiki/Main_Page)
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
---
2+
sidebar_position: 1
3+
---
4+
# bitcoinjs-lib
5+
6+
sCrypt exports a submodule named `bitcoinjs-lib` which is an interface that helps you manage low-level things for the Bitcoin blockchain, such as creating key pairs, building, signing and serializing Bitcoin transactions, and more.
7+
8+
In the context of sCrypt, the `bitcoinjs-lib` submodule is used primarily for managing key pairs and defining custom transaction builders, as demonstrated in the [How to Write a Contract](../how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx.md) section.
9+
10+
The goal of this section is to guide you through the basics of using the `bitcoinjs-lib` submodule.
11+
12+
## Importing
13+
14+
You can import the `bitcoinjs-lib` submodule like this:
15+
16+
```ts
17+
import * as bitcoinjs from '@scrypt-inc/bitcoinjs-lib'
18+
```
19+
20+
## Private Keys
21+
22+
23+
You can generate a Bitcoin private key (for `mainnet`) from a random value like this:
24+
25+
```ts
26+
import * as ecc from '@bitcoinerlab/secp256k1';
27+
import * as bitcoinjs from '@scrypt-inc/bitcoinjs-lib'
28+
import ECPairFactory from 'ecpair';
29+
const ECPair = ECPairFactory(ecc);
30+
const keyPair = ECPair.makeRandom({ network: bitcoinjs.networks.bitcoin });
31+
const wif = keyPair.toWIF();
32+
console.log(`Private key: ${wif}`);
33+
const { address } = bitcoinjs.payments.p2pkh({ pubkey: keyPair.publicKey });
34+
console.log(`Address: ${address}`);
35+
```
36+
37+
To create a private key for the test network (also referred to as `testnet`), do the following instead:
38+
39+
```ts
40+
const keyPair = ECPair.makeRandom({ network: bitcoinjs.networks.testnet });
41+
const wif = keyPair.toWIF();
42+
console.log(`Private key: ${wif}`);
43+
```
44+
45+
The main difference between a mainnet and a testnet key is how they get serialized.
46+
Check out this [Bitcoin Wiki page on WIFs](https://en.bitcoin.it/wiki/Wallet_import_format) which explains the differences in more detail.
47+
48+
You can also create `PrivateKey` objects from serialized keys like this:
49+
50+
```ts
51+
const keyPair = ECPair.fromWIF('cVDFHtcTU1wn92AkvTyDbtVqyUJ1SFQTEEanAWJ288xvA7TEPDcZ');
52+
const keyPair = ECPair.fromPrivateKey(Buffer.from('e3a9863f4c43576cdc316986ba0343826c1e0140b0156263ba6f464260456fe8', 'hex'));
53+
```
54+
55+
:::warning
56+
Private keys should be carefully stored and never be publicly revealed. Otherwise it may lead to loss of funds.
57+
:::
58+
59+
60+
## Public Keys
61+
62+
A public key is derived from a private key and can be shared publicly. Mathematically, a public key is a point on the default elliptic curve that Bitcoin uses, named [`SECP256K1`](https://en.bitcoin.it/wiki/Secp256k1). It is the curve's base point multiplied by the value of the private key.
63+
64+
You can get the public key corresponding to a private key the following way:
65+
66+
```ts
67+
const keyPair = ECPair.makeRandom({ network: bitcoinjs.networks.bitcoin });
68+
const pubKey = keyPair.publicKey
69+
```
70+
71+
72+
## Addresses
73+
74+
You can get a Bitcoin address from either the private key or the public key:
75+
76+
```ts
77+
const keyPair = ECPair.makeRandom({ network: bitcoinjs.networks.bitcoin });
78+
const { address } = bitcoinjs.payments.p2pkh({ pubkey: keyPair.publicKey });
79+
console.log(`P2PKH Address: ${address}`);
80+
// Address: 19oTCSHG5o8Mdnx9cZ5f7tZ4nxSPCVTgM4
81+
const { address } = bitcoinjs.payments.p2wpkh({ pubkey: keyPair.publicKey });
82+
console.log(`P2WPKH Address: ${address}`);
83+
// Address: bc1qvz9qys2v0qwjhvtk8jy33p7tpffxtt797yhh5m
84+
bitcoinjs.initEccLib(ecc);
85+
const { address } = bitcoinjs.payments.p2tr({ pubkey: bitcoinjs.toXOnly(keyPair.publicKey) });
86+
console.log(`P2TR Address: ${address}`);
87+
// Address: bc1p56rmppfnud745ml4ez654xrf6n00n0wz5jlccwjm8v6d3y6ve5tsg6zncq
88+
```
89+
90+
Read this [Bitcoin wiki page](https://en.bitcoin.it/wiki/Invoice_address) for more information on how Bitcoin addresses are constructed.
91+
92+
93+
## Constructing Transactions
94+
95+
The `bitcoinjs-lib` submodule offers a flexible system for constructing Bitcoin transactions. Users are able to define scripts, transaction inputs and outputs, and a whole transaction including its metadata. For a complete description of Bitcoin's transaction format, please read the [Bitcoin wiki page](https://en.bitcoin.it/wiki/Transaction).
96+
97+
As an exercise let's construct a simple [P2PKH](https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash) transaction from scratch and sign it.
98+
99+
:::note
100+
As you will notice further in these docs, most of these steps won't be needed in a regular smart contract development workflow as sCrypt already does a lot of heavy lifting for you. This section serves more as a deeper look on what is happening under the hood.
101+
:::
102+
103+
You can create an empty psbt like this:
104+
```ts
105+
import {
106+
ExtPsbt
107+
} from '@scrypt-inc/scrypt-ts-btc'
108+
const psbt = new ExtPsbt();
109+
```
110+
111+
Because the transaction will need an input that provides it with some funds, we can use the `from` function to add one that unlocks the specified [UTXO](https://en.bitcoin.it/wiki/UTXO):
112+
113+
```ts
114+
115+
psbt.addInput({
116+
// if hash is string, txid, if hash is Buffer, is reversed compared to txid
117+
hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e',
118+
index: 0,
119+
sequence: 0xffffffff, // These are defaults. This line is not needed.
120+
// non-segwit inputs now require passing the whole previous tx as Buffer
121+
nonWitnessUtxo: Buffer.from(rawTxHex, 'hex')
122+
});
123+
```
124+
125+
Now, the transaction needs an output that will pay to the address `mxXPxaRvFE3178Cr6KK7nrQ76gxjvBQ4UQ` in our example:
126+
127+
```ts
128+
psbt.addOutput({
129+
address: 'mxXPxaRvFE3178Cr6KK7nrQ76gxjvBQ4UQ',
130+
value: 80000n,
131+
});
132+
```
133+
134+
Notice how the output value is 100 satoshis less than the value of the UTXO we're unlocking. This difference is the [transaction fee](https://wiki.bitcoinsv.io/index.php/Transaction_fees) (sometimes also called the "miner fee"). The transaction fees are collected by miners when they mine a block, so adding a transaction fee basically acts as an incentive for miners to include your transaction in a block.
135+
136+
The amount of transaction fee you should pay depends on the fee rate and the bytes of the transaction. By adding an additional output to the transaction, we can control how much the transaction fee is actually paid. This output is called the change output. By adjusting the amount of change output, we can pay as little transaction fees as possible while meeting the needs of miners.
137+
138+
You can directly call the `change` function to add a change output to the transaction without calculating the change amount by yourself. This function is smart enough that it will only add the change output when the difference between all inputs and outputs is more than the required transaction fee.
139+
140+
```ts
141+
const feePerKb = 1;
142+
psbt.change('n4fTXc2kaKXHyaxmuH5FTKiJ8Tr4fCPHFy', feePerKb)
143+
```
144+
145+
146+
### Signing
147+
148+
Now that we have the transaction constructed, it's time to sign it. First, we need to create a Signer, so it will be ready to sign. Then we call the `signPsbt` function of the Signer. After getting signedPsbt, combine it and finalize all inputs.
149+
150+
```ts
151+
const signer = new DefaultSigner()
152+
const signedPsbtHex = await signer.signPsbt(psbt.toHex(), psbt.psbtOptions());
153+
const signedPsbt = psbt.combine(ExtPsbt.fromHex(signedPsbtHex)).finalizeAllInputs();
154+
```
155+
156+
Viola! That's it. This will add the necessary data to the transaction's input script: the signature and the public key of our signing key. Now our transaction is ready to be posted to the blockchain.
157+
158+
You can serialize the transaction like this:
159+
160+
```ts
161+
const tx = signedPsbt.extractTransaction();
162+
console.log(tx.toHex())
163+
```
164+
165+
To broadcast a transaction, you can use any provider you like.
166+
For demo and test purposes you can paste the serialized transaction [here](https://mempool.space/tx/push).
167+
168+
169+
## References
170+
171+
- Take a look at the full [`bitcoinjs-lib` submodule reference](../references/bitcoinjs-lib) for a full list of what functions it provides.
172+
- As the `@scrypt-inc/bitcoinjs-lib` a fork based on [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib) implementation, take a look at their [github](https://github.com/bitcoinjs/bitcoinjs-lib).
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
sidebar_position: 7
3+
---
4+
5+
# How to Debug a Contract
6+
7+
Debugging a sCrypt contract is as easy as debugging TypeScript, since it is just TypeScript.
8+
9+
10+
## Use console.log()
11+
12+
You can use `console.log()` to print output to the console.
13+
14+
15+
```ts
16+
export class Demo extends SmartContract {
17+
18+
@prop()
19+
readonly x: bigint
20+
21+
@prop()
22+
readonly y: bigint
23+
24+
constructor(x: bigint, y: bigint) {
25+
super(...arguments)
26+
this.x = x
27+
this.y = y
28+
}
29+
30+
@method()
31+
sum(a: bigint, b: bigint): bigint {
32+
return a + b
33+
}
34+
35+
@method()
36+
public add(z: bigint) {
37+
console.log(`z: ${z}`) // print the value of z
38+
console.log(`sum: ${this.x + this.y}`) // print the value of this.x + this.y
39+
assert(z == this.sum(this.x, this.y), 'incorrect sum')
40+
}
41+
}
42+
```
43+
[Try it on Replit](https://replit.com/@msinkec/scryptTS-console-logging)
44+
45+
After running the code, you should see the following output:
46+
47+
```
48+
z: 3
49+
sum: 3
50+
```
51+
52+
53+
## Use Visual Studio Code debugger
54+
55+
You can use VS Code to debug sCrypt contracts, the same way as any other TypeScript programs. To learn more about the VS Code TypeScript debugger, please refer to the [official documentation](https://code.visualstudio.com/docs/TypeScript/TypeScript-debugging).
56+
57+
## Debug a ScriptContext Failure
58+
One common failure is caused by IContext assertions, like
59+
```typescript
60+
assert(this.ctx.shaOutputs == sha256(outputs), 'shaOutputs mismatch')
61+
```
62+
Refer to [this guide](advanced/how-to-debug-scriptcontext.md) to debug such failures.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"position": 4,
3+
"label": "How to Deploy & Call a Contract",
4+
"collapsible": true,
5+
"collapsed": true
6+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
sidebar_position: 3
3+
---
4+
5+
# Deploy Using CLI
6+
7+
The `deploy` command allows you to deploy an instance of a smart contract to the blockchain. You can simply run the following command in the root of an `sCrypt` project:
8+
9+
```sh
10+
npx @scrypt-inc/cli-btc deploy
11+
```
12+
13+
or
14+
15+
```sh
16+
npx @scrypt-inc/cli-btc d
17+
```
18+
19+
By default, the CLI tool will run a script named `deploy.ts` located in the root of the project. You can also specify a different deployment script using the `--file` or `-f` option.
20+
21+
```sh
22+
npx @scrypt-inc/cli-btc d -f myCustomDeploy.ts
23+
```
24+
25+
If the project was created using sCrypt CLI, it will already have a `deploy.ts` file present (except for [library](../how-to-publish-a-contract.md) projects). If not, the `deploy` command will generate a sample `deploy.ts` file.
26+
27+
Here's an example of such a deployment file:
28+
```ts
29+
import { Demo } from './src/contracts/demo'
30+
import * as dotenv from 'dotenv'
31+
import { getDefaultProvider, getDefaultSigner } from './tests/utils/txHelper';
32+
import { readArtifact } from '@scrypt-inc/scrypt-ts-transpiler-btc';
33+
import { Covenant, deploy, sha256, toByteString } from '@scrypt-inc/scrypt-ts-btc';
34+
35+
import * as dotenv from 'dotenv'
36+
37+
// Load the .env file
38+
dotenv.config()
39+
40+
async function main() {
41+
const artifact = readArtifact(Demo);
42+
Demo.loadArtifact(artifact)
43+
const covenant = Covenant.createCovenant(new Demo(sha256(toByteString("hello world", true))))
44+
45+
const provider = getDefaultProvider();
46+
const signer = getDefaultSigner();
47+
48+
const deployTx = await deploy(signer, provider, covenant);
49+
50+
console.log(`Demo contract deployed: ${deployTx.getId()}`)
51+
}
52+
53+
main()
54+
```
55+
56+
Upon a successful execution you should see an output like the following:
57+
58+
```
59+
Demo contract deployed: 15b8055cfaf9554035f8d3b866f038a04e40b45e28109f1becfe4d0af9f743cd
60+
```
61+
62+
You can take a look at the deployed smart contract using the [WhatsOnChain block explorer](https://test.whatsonchain.com/tx/15b8055cfaf9554035f8d3b866f038a04e40b45e28109f1becfe4d0af9f743cd).
63+
In our example, the first output contains the compiled smart contract code.
64+
It is indexed using the hash (double SHA-256) of the script: [eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def](https://test.whatsonchain.com/script/eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def).
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
sidebar_position: 0
3+
---
4+
5+
# Faucet
6+
7+
It is highly recommended to test your contract on the following testnets after passing local tests.
8+
9+
1. [BTC signet](https://mempool.space/signet)
10+
2. [Fractal testnet](https://mempool-testnet.fractalbitcoin.io/)
11+
12+
13+
It ensures that a contract can be successfully deployed and invoked as expected on the blockchain.
14+
15+
Before you deploy and call a contract, you need to have a funded address:
16+
17+
18+
1. Create a new project. Skip this step if you have already created a project:
19+
20+
```sh
21+
npx @scrypt-inc/cli-btc project demo
22+
cd demo
23+
```
24+
25+
2. Generate a private key with the following command executed from the root of the project:
26+
27+
```sh
28+
npm install
29+
npm run genprivkey
30+
```
31+
32+
The command generates a private key and stores it in a `.env` file in the project's root directory.
33+
It also outputs the [Bitcoin address](https://en.bitcoin.it/wiki/Invoice_address) corresponding to the private key.
34+
35+
3. Fund the private key's address with some testnet coins. You can use these faucets to receive testnet coins.
36+
37+
1. [BTC signet faucets](https://en.bitcoin.it/wiki/Signet#Faucets)
38+
2. [fractal testnet faucet](https://fractal-testnet.unisat.io/explorer/faucet)

0 commit comments

Comments
 (0)