The Complete Guide To FullStack BSV Development.
Tags: webdev, blockchain, bsv, scrypt
Published: 2024-04-11 03:01:45


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/imtf2k43p3avmikr43yl.png)
<h2>Tic Tac Toe On Bitcoin SV Blockchain</h2>
[Bitcoin SV (BSV)](https://www.bsvblockchain.org/) blockchain upholds the original vision of Bitcoin as outlined by Satoshi Nakamoto in the bitcoin whitepaper. Bitcoin SV distinguishes itself by providing a stable and scalable foundation for building dApps, leveraging it robust infrastructure and unique features.<br>
In this comprehensive guide, we will delve into building a decentralized [Tic-Tac-Toe](https://docs.scrypt.io/tutorials/tic-tac-toe/) game on the Bitcoin SV blockchain using sCrypt. From setting up the development environment to writing the smartcontracts and frontend integration, we will provide step-by-step instructions, practical insights to help you navigate the exciting world of Bitcoin SV dApp development using sCrypt. 

![description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/96hqmvok44g3nis95j3o.jpg)
The game is initialized with the Bitcoin public key of two players (Alice and Bob respectively). They each bet the same amount and lock it into the contract. The winner takes all bitcoins locked in the contract. If no one wins and there is a draw, the two players can each withdraw half of the money.
<h2>sCrypt Overview</h2>
`sCrypt` is an `embedded Domain Specific Language` (eDSL) based on TypeScript for writing smart contracts on Bitcoin SV. sCrypt simplifies the process of writing, testing, and deploying smart contracts, empowering developers to build secure and scalable applications on the Bitcoin SV network.
<h3>Prerequisite to Get Started</h3>
Make sure all prerequisite are install;

1. Install `Node.js` (require version `>=16`) and `NPM` on your machine by following the instructions over [here](https://nodejs.org/en/download)

2. Install [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)

3. Next, install the sCrypt Cli: follow this command to install the sCrypt Cli globally to your machine
`npm install -g scrypt-cli`
 The tools we will be using for this project include;

- 
[sCrypt language](https://scrypt.io/): a typescript base framework for writing smartcontract on Bitcoin SV with a high-level abstraction. It’s a static –typed language which provides a safety and easy to use.

- 
[sCrypt library](https://github.com/sCrypt-Inc/scrypt-ord): a comprehensive library for client-side seamless interaction with the Bsv blockchain

- 
[sCrypt Cli](https://scrypt.io/): to easily create and compile sCrypt into bitcoin script and provides a best-practice scaffolding to help developers follow the recommended standards and conventions for writing sCrypt code.

- 
React: the client-side framework.

- 
[Yours wallet](https://chrome.google.com/webstore/detail/yours-wallet/mlbnicldlpdimbjdcncnklfempedeipj):Yours Wallet is an open-source, non-custodial Bitcoin SV web3 wallet designed for ease of use and everyday transactions. Yours offers a straightforward and secure way to receive, send, and manage your digital assets.

<h3>Getting Started</h3>
 create a new React project with TypeScript template
`npx create-react-app tic tac toe --template typescript`
add sCrypt dependencies to your project.
`cd tic-tac-toe
npx scrypt-cli@latest init`
<h2>Creating Tic Tac Toe Contract</h2>
create a contract at `src/contracts/tictactoe.ts`
```javascript
import {
    prop, method, SmartContract, PubKey, FixedArray, assert, Sig, Utils, toByteString, hash160,
    hash256,
    fill,
    ContractTransaction,
    MethodCallOptions,
    bsv
} from "scrypt-ts";

export class TicTacToe extends SmartContract {
    @prop()
    alice: PubKey;
    @prop()
    bob: PubKey;
    @prop(true)
    isAliceTurn: boolean;
    @prop(true)
    board: FixedArray<bigint, 9>;
    static readonly EMPTY: bigint = 0n;
    static readonly ALICE: bigint = 1n;
    static readonly BOB: bigint = 2n;

    constructor(alice: PubKey, bob: PubKey) {
        super(...arguments)
        this.alice = alice;
        this.bob = bob;
        this.isAliceTurn = true;
        this.board = fill(TicTacToe.EMPTY, 9);
    }

    @method()
    public move(n: bigint, sig: Sig) {
        // check position `n`
        assert(n >= 0n && n < 9n);
        // check signature `sig`
        let player: PubKey = this.isAliceTurn ? this.alice : this.bob;
        assert(this.checkSig(sig, player), `checkSig failed, pubkey: ${player}`);
        // update stateful properties to make the move
        assert(this.board[Number(n)] === TicTacToe.EMPTY, `board at position ${n} is not empty: ${this.board[Number(n)]}`);
        let play = this.isAliceTurn ? TicTacToe.ALICE : TicTacToe.BOB;
        this.board[Number(n)] = play;
        this.isAliceTurn = !this.isAliceTurn;

        // build the transation outputs
        let outputs = toByteString('');
        if (this.won(play)) {
            outputs = Utils.buildPublicKeyHashOutput(hash160(player), this.ctx.utxo.value);
        }
        else if (this.full()) {
            const halfAmount = this.ctx.utxo.value / 2n;
            const aliceOutput = Utils.buildPublicKeyHashOutput(hash160(this.alice), halfAmount);
            const bobOutput = Utils.buildPublicKeyHashOutput(hash160(this.bob), halfAmount);
            outputs = aliceOutput + bobOutput;
        }
        else {
            // build a output that contains latest contract state.
            outputs = this.buildStateOutput(this.ctx.utxo.value);
        }
        if (this.changeAmount > 0n) {
            outputs += this.buildChangeOutput();
        }
        // make sure the transaction contains the expected outputs built above
        assert(this.ctx.hashOutputs === hash256(outputs), "check hashOutputs failed");
    }

    @method()
    won(play: bigint): boolean {
        let lines: FixedArray<FixedArray<bigint, 3>, 8> = [
            [0n, 1n, 2n],
            [3n, 4n, 5n],
            [6n, 7n, 8n],
            [0n, 3n, 6n],
            [1n, 4n, 7n],
            [2n, 5n, 8n],
            [0n, 4n, 8n],
            [2n, 4n, 6n]
        ];
        let anyLine = false;
        for (let i = 0; i < 8; i++) {
            let line = true;
            for (let j = 0; j < 3; j++) {
                line = line && this.board[Number(lines[i][j])] === play;
            }
            anyLine = anyLine || line;
        }
        return anyLine;
    }

    @method()
    full(): boolean {
        let full = true;
        for (let i = 0; i < 9; i++) {
            full = full && this.board[i] !== TicTacToe.EMPTY;
        }
        return full;
    }

}
```
<h3>Contract Properties</h3>
The tic-tac-toe contract supports two players and their public keys need to be saved. It contains the following contract properties:

- 
Two stateless properties `alice` and `bob`, both of which are PubKey type.

- 
Two stateful properties:

- 
`is_alice_turn`: a boolean. It represents whether it is alice's turn to play.

- 
`board`: a fixed-size array FixedArray<bigint, 9> with a size of 9. It represents the state of every square in the board.

- 
Three constants:

- 
`EMPTY`, type bigint, value 0n. It means that a square in the board is empty

- 
`ALICE`, type bigint, value 1n. Alice places symbol X in a square.
`BOB`, type bigint, value 2n. Bob places symbol O in a square.
<h3>Constructor</h3>
all the non-static properties are initialized in the constructor. at first, the entire board is empty.
```javascript
constructor(alice: PubKey, bob: PubKey) {
    super(...arguments);
    this.alice = alice;
    this.bob = bob;
    this.is_alice_turn = true;
    this.board = fill(TicTacToe.EMPTY, 9);
}
```
<h3>Public Methods</h3>
The TicTacToe contract contains a public`@method `named `move()`, which accepts two parameters. `Alice` and `Bob` engage in the game by sequentially invoking `move()` after each locking X bitcoins in a UTXO associated with the `TicTacToe`contract.
```javascript
@method()
public move(n: bigint, sig: Sig) {
    assert(n >= 0n && n < 9n);
}
```
<br>After deploying the game contract, it becomes publicly accessible for viewing and potential interaction. To ensure that only the intended player can update the contract when it's their turn, an authentication mechanism is required. This is accomplished through the use of digital signatures.
`this.checkSig()` is used to verify a signature against a public key. Use it to verify the sig parameter against the desired player in `move()`, identified by their public key stored in the contract's properties.
```javascript
// check signature `sig`
let player: PubKey = this.is_alice_turn ? this.alice : this.bob;
assert(this.checkSig(sig, player), `checkSig failed, pubkey: ${player}`);
```
<h3>Non-Public Methods</h3>
The `TicTacToe` contract have two Non-Public methods:

- 
`won()` : iterate over the `lines` array to check if a player has won the game. returns `boolean` type.

- 
`full()` : traverse all the squares of the board to check if all squares of the board have symbols. returns `boolean` type.

<h3>Customise a contract transaction builder</h3>
we need to customise a transaction builder for our public method `move()`
```javascript
static buildTxForMove(
    current: TicTacToe,
    options: MethodCallOptions<TicTacToe>,
    n: bigint
  ): Promise<ContractTransaction> {
    const play = current.isAliceTurn ? TicTacToe.ALICE : TicTacToe.BOB;
    const nextInstance = current.next();
    nextInstance.board[Number(n)] = play;
    nextInstance.isAliceTurn = !current.isAliceTurn;
    const unsignedTx: bsv.Transaction = new bsv.Transaction().addInput(
      current.buildContractInput(options.fromUTXO)
    );
    if (nextInstance.won(play)) {
      const script = Utils.buildPublicKeyHashScript(
        hash160(current.isAliceTurn ? current.alice : current.bob)
      );
      unsignedTx.addOutput(
        new bsv.Transaction.Output({
          script: bsv.Script.fromHex(script),
          satoshis: current.balance,
        })
      );
      if (options.changeAddress) {
        unsignedTx.change(options.changeAddress);
      }
      return Promise.resolve({
        tx: unsignedTx,
        atInputIndex: 0,
        nexts: [],
      });
    }
    if (nextInstance.full()) {
      const halfAmount = current.balance / 2;
      unsignedTx
        .addOutput(
          new bsv.Transaction.Output({
            script: bsv.Script.fromHex(
              Utils.buildPublicKeyHashScript(hash160(current.alice))
            ),
            satoshis: halfAmount,
          })
        )
        .addOutput(
          new bsv.Transaction.Output({
            script: bsv.Script.fromHex(
              Utils.buildPublicKeyHashScript(hash160(current.bob))
            ),
            satoshis: halfAmount,
          })
        );
      if (options.changeAddress) {
        unsignedTx.change(options.changeAddress);
      }
      return Promise.resolve({
        tx: unsignedTx,
        atInputIndex: 0,
        nexts: [],
      });
    }
    unsignedTx.setOutput(0, () => {
      return new bsv.Transaction.Output({
        script: nextInstance.lockingScript,
        satoshis: current.balance,
      });
    });
    if (options.changeAddress) {
      unsignedTx.change(options.changeAddress);
    }
    const nexts = [
      {
        instance: nextInstance,
        atOutputIndex: 0,
        balance: current.balance,
      },
    ];
    return Promise.resolve({
      tx: unsignedTx,
      atInputIndex: 0,
      nexts,
      next: nexts[0],
    });
  }
```
<h2>Integrate front-end</h2>
after writing and testing our contract, the front-end gives user t an interface to interact and play the the Tic Tac Toe game. However, we need to compile our contract using this line of code:
`npx scrypt-cli@latest compile`<br>
this should generate `tictactoe.json` artifact file in the `artifact directory` which can be further use to initialize the contract at the frontend.
```javascript
import { TicTacToe } from './contracts/tictactoe';
import artifact from '../artifacts/tictactoe.json';

TicTacToe.loadArtifact(artifact);
```
<h3>Connect to wallet</h3>
before deploying the contract, you need to connect to a wallet. but first thing first, 

- 
install  [yours wallet](https://chromewebstore.google.com/detail/yours-wallet/mlbnicldlpdimbjdcncnklfempedeipj?pli=1)

- 
After installing the wallet, click the `settings` button in the upper right corner to switch to `testnet`.

![description1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t0o33i22cazxz8pjukxa.PNG)
![description2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ginlbzjy19nlfne6jh8f.PNG)
Then copy your wallet address and follow the guide [here](https://scrypt.io/faucet) to get funded.

![description3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a8yrmstre675taowx0sx.PNG)
so, after that been done, you can request access to the wallet, you can use its `requestAuth` method and also call `getDefaultPubKey()` to get its public key.
```javascript
const walletLogin = async () => {
    try {
      const provider = new DefaultProvider({
          network: bsv.Networks.testnet
      });
      const signer = new PandaSigner(provider);
      signerRef.current = signer;

      const { isAuthenticated, error } = await signer.requestAuth()
      if (!isAuthenticated) {
        throw new Error(error)
      }
      setConnected(true);
      const alicPubkey = await signer.getDefaultPubKey();
      setAlicePubkey(toHex(alicPubkey))
      // Prompt user to switch accounts
    } catch (error) {
      console.error("pandaLogin failed", error);
      alert("pandaLogin failed")
    }
};
```
<h3>Initialize the contract</h3>
the contract is initialized with the public keys of two players `alice` and `bob`. The public key can be obtained through calling `getDefaultPubKey()` of Signer.

The following code initializes the contract.
```javascript
const [alicePubkey, setAlicePubkey] = useState("");
const [bobPubkey, setBobPubkey] = useState("");
...
const startGame = async (amount: number) => {
  try {
    const signer = signerRef.current as PandaSigner;
    const instance = new TicTacToe(
        PubKey(toHex(alicePubkey)),
        PubKey(toHex(bobPubkey))
      );
    await instance.connect(signer);

  } catch(e) {
    console.error('deploy TicTacToe failes', e)
    alert('deploy TicTacToe failes')
  }
};
```
<h3>Call the contract</h3>
```javascript
const { tx: callTx } = await p2pkh.methods.unlock(
    (sigResponses: SignatureResponse[]) => findSig(sigResponses, $publickey),
    $publickey,
    {
        pubKeyOrAddrToSign: $publickey.toAddress()
    } as MethodCallOptions<P2PKH>
);
```
Every move is a call to the contract and causes a change in the state of the contract.

Once you've completed working on the front-end, you can simply execute it by running
```npm start```
You can then access it through your browser at `http://localhost:3000/.`
<h2>Conclusion</h2>
Through this comprehensive guide, we've navigated the intricate landscape of BSV blockchain dAPP development using sCrypt, from smart contract creation to front-end integration, unlocking the potential for endless innovation using sCrypt on BSV blockchain. learn more about sCrypt from its comprehensive documentation [here](https://docs.scrypt.io/)