# Your First BSV App

Canonical: https://docs.projectbabbage.com/docs/quickstarts/your-first-bsv-app

A step-by-step tutorial for developers looking to create their first BSV application, covering everything from project setup through transaction creation and wallet integration—all built on the BRC specifications for maximum interoperability. Then, deploy your app online!

Let's build a simple **ToDo List** application on the BSV Blockchain — complete with tokenized tasks, encryption, and a React UI! By the end, you’ll have a functioning local development setup that uses the [TypeScript SDK](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/sdk) and a [**BRC-100**](https://github.com/bitcoin-sv/BRCs/blob/master/wallet/0100.md)-compliant wallet (via `WalletClient`) to create and inspect encrypted ToDo items. (BRC-100 defines wallet-to-app communication)

You’ll also see how these tasks reside as tokens in a “basket,” demonstrating basic UTXO-based asset tokenization.

## What You’ll Build
![todo-app-launch](https://docs.projectbabbage.com/assets/images/todo-app-launch.webp)

A simple ToDo List app, where:
- You create a *ToDo token* by setting aside some satoshis.
- The “task” text is *encrypted* (so only you can read it).
- You inspect the created token and learn where redemption fits as an advanced follow-up.

Your tasks each live as a separate unspent transaction output (UTXO) in a special “basket,” representing them as tokens on the blockchain. You’ll also see the full [**BRC-102**](https://github.com/bitcoin-sv/BRCs/blob/master/apps/0102.md) structure, meaning you’ll have:
- A **frontend/** directory containing a React application,
- A **deployment-info.json** describing what to run locally,
- Minimal setup for local dev ([LARS](https://github.com/p2ppsr/lars)) and an example “ToDo tokens” basket usage.

## Prerequisites
1. [Node.js](https://nodejs.org/) 22 or 24 (maintained and patched) installed. (check with `node -v` and `npm -v`).
2. A **BSV Wallet** installed and running locally. [Go here to get one](https://docs.projectbabbage.com/docs/quickstarts/getting-a-wallet)!
3. Basic TypeScript, Node, React, and terminal skills.

## Why It’s Interesting
- Showcases the **UTXO model**: Each task is a token that lives in your wallet “basket.”
- Uses the `WalletClient` from the [BSV TypeScript SDK](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/sdk) for encryption, signature creation, and more.
- Demonstrates a minimal “contract” with an encrypted data field, “PushDrop,” stored on-chain.
- Illustrates the [**BRC-100**](https://github.com/bitcoin-sv/BRCs/blob/master/wallet/0100.md) approach to “wallet → app” communications.

## Project Setup

Below is a skeleton of the [**BRC-102**](https://github.com/bitcoin-sv/BRCs/blob/master/apps/0102.md) structure you’ll have. Create a new folder, for example named `todo-bsv/`:

```
todo-bsv/
 ├─ deployment-info.json
 ├─ frontend/
    ├─ src/
       ├─ index.tsx
       ├─ theme.ts
       ├─ App.tsx
       └─ ...
    ├─ index.html
    ├─ package.json
    └─ vite.config.ts
 ├─ .gitignore
 └─ package.json
```

> **deployment-info.json** will instruct local dev tools *what to run* and *how to run it*, following the [**BRC-102**](https://github.com/bitcoin-sv/BRCs/blob/master/apps/0102.md) standard.

### Initialize

From an empty folder, run:
```bash
npm init -y
```
This generates your root `package.json`.

### Add Your Tools

Install the local LARS runtime for development, and CARS for deploying your app later:

```bash
npm install --save-dev @p2ppsr/lars@latest @p2ppsr/cars-cli@latest
```

Then, you should edit your new `package.json` file to replace the `scripts` section with this `lars/cars` enhanced version:

```json
"scripts": {
  "lars": "lars",
  "cars": "cars",
  "lars:config": "lars config",
  "start": "lars start",
  "build": "npm --prefix frontend run build",
  "deploy": "echo \"Add a CARS deployment config before deploying\" && exit 1",
  "test": "echo \"Error: no test specified\" && exit 1"
}
```

If you're using git, you'll also want to create a `.gitignore` file to ignore unnecessary files. You can use this full version of the file [download from here](https://github.com/p2ppsr/meter/blob/master/.gitignore). Or you could use this local command or similar, it is important to have this file if you use git to avoid potential version control issues:

```bash
printf "node_modules/\ndist/\nbuild/\n.env\n" > .gitignore
```

For a minimal React app, create a `frontend/` directory and then you can add the additional files you require and install the dependencies using this script or similar:

```bash
cd frontend
npm init -y
touch vite.config.ts
touch index.html
mkdir src
touch src/index.tsx
touch src/theme.ts
touch src/App.tsx
npm init -y
npm install react react-dom @bsv/sdk @mui/system @emotion/react @emotion/styled @mui/material @mui/icons-material react-toastify
npm install --save-dev vite typescript @types/react @types/react-dom @vitejs/plugin-react
```

You have now installed `@bsv/sdk` (the [BSV TypeScript SDK](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/sdk)), `vite` for development, and [MUI](https://mui.com) for your UI.

Next, update the `frontend/package.json` to include scripts for running and building the app. Open `frontend/package.json` and ensure the `scripts` section looks like this:

```json
"scripts": {
  "start": "vite",
  "build": "vite build",
  "test": "echo \"Error: no test specified\" && exit 1"
}
```

This ensures LARS can start the frontend using `npm start` in the `frontend/` directory.

**Complete `frontend/package.json` (Optional Reference)**:
If you prefer to copy a complete `frontend/package.json`, here’s what it should look like after the above steps:

```json
{
  "name": "frontend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "vite",
    "build": "vite build",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "react": "latest",
    "react-dom": "latest",
    "@bsv/sdk": "latest",
    "@mui/system": "latest",
    "@emotion/react": "latest",
    "@emotion/styled": "latest",
    "@mui/material": "latest",
    "@mui/icons-material": "latest",
    "react-toastify": "latest"
  },
  "devDependencies": {
    "vite": "latest",
    "typescript": "latest",
    "@types/react": "latest",
    "@types/react-dom": "latest",
    "@vitejs/plugin-react": "latest"
  }
}
```

Alternatively, you can find the current `package.json` for the `todo-ts` repo [here](https://github.com/p2ppsr/todo-ts/blob/master/package.json).

You have now installed `@bsv/sdk` (the [BSV TypeScript SDK](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/sdk)), `vite` for development, and [MUI](https://mui.com) for your UI.

## deployment-info.json

In your **project root**, create **deployment-info.json**:

```json
{
  "schema": "bsv-app",
  "schemaVersion": "1.0",
  "frontend": {
    "language": "react",
    "sourceDirectory": "./frontend"
  },
  "configs": [
    {
      "name": "Local LARS",
      "provider": "LARS",
      "network": "mainnet",
      "run": ["frontend"]
    }
  ]
}
```

This says:
- This project follows the `bsv-app` schema ([**BRC-102**](https://github.com/bitcoin-sv/BRCs/blob/master/apps/0102.md)).
- The project has a React frontend in `frontend/`.
- The project has one local config named `"Local LARS"` that runs the frontend.

**Why “mainnet” here?**
You can set it to `"testnet"` if your wallet and environment use testnet. The [BSV TypeScript SDK](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/sdk) allows either, as long as the wallet is configured accordingly.

## Creating the React App

Within **frontend/**, put a minimal `vite.config.ts`:

```ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  build: {
    outDir: 'build' // required for LARS to run
  }
})
```

Add this into your `index.html` file:

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>My First BSV App</title>
</head>
<body>
  <div id="root"></div>
  <script type="module" src="/src/index.tsx"></script>
</body>
</html>
```

Then add this TypeScript code to your source files as shown below:

### `frontend/src/index.tsx`

```ts
import React from 'react'
import ReactDOM from 'react-dom/client'
import { CssBaseline } from '@mui/material'
import { ThemeProvider } from '@mui/material/styles'
import App from './App'
import web3Theme from './theme'
import { ToastContainer } from 'react-toastify'
import 'react-toastify/dist/ReactToastify.css'

const rootElement = document.getElementById('root')

if (rootElement === null) {
  throw new Error('Failed to find the root element')
}

const root = ReactDOM.createRoot(rootElement)

root.render(
  <ThemeProvider theme={web3Theme}>
    <ToastContainer
      position="top-center"
      autoClose={5000}
      hideProgressBar={false}
      newestOnTop={false}
      closeOnClick
      pauseOnFocusLoss
      draggable
      pauseOnHover
    />
    <CssBaseline />
    <App />
  </ThemeProvider>
)
```

### `frontend/src/theme.ts`

```ts
import { createTheme } from '@mui/material/styles'

// Define a custom theme with a dark background

// Define custom spacing function compatible with Spacing type
const customSpacing = (factor: number): string => `${8 * factor}px`

// Create custom theme using createTheme
const web3Theme = createTheme({
  palette: {
    mode: 'dark',
    primary: {
      main: '#00d1b2'
    },
    secondary: {
      main: '#7e57c2'
    },
    background: {
      default: '#121212',
      paper: '#242424'
    },
    error: {
      main: '#ff3860'
    }
  },
  typography: {
    fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
    h4: {
      fontWeight: 700
    },
    button: {
      textTransform: 'none'
    }
  },
  // Override default MUI spacing with custom spacing function
  spacing: customSpacing
})

export default web3Theme // Export custom theme
```

Now you have all the basic structure covered. Time to build!

Note: *The code below implements the task creation functionality, allowing you to encrypt a task and lock satoshis into a token on the BSV blockchain. This initial version focuses solely on creating ToDo items. Later, we’ll provide an updated version with a redemption feature as an optional exercise for you to complete.*

### `frontend/src/App.tsx`

```tsx
import React, { useState, ChangeEvent } from 'react'
import { WalletClient, Utils, PushDrop } from '@bsv/sdk'

/**
 * This UI demonstrates a simple "create ToDo item" functionality stored on the blockchain.
 * The user's BSV wallet provides encryption keys for securing transactions via WalletClient.
 * Tasks are encrypted, locked into a PushDrop script, and broadcast as a transaction.
 */
const walletClient = new WalletClient('auto')

export default function App (): JSX.Element {
  const [taskDescription, setTaskDescription] = useState<string>('')
  const [taskValue, setTaskValue] = useState<number>(1000) // Default satoshis to lock

  /**
   * Handles creating a new ToDo task by encrypting the description,
   * locking it in a PushDrop script, and broadcasting it to the blockchain.
   * @returns {Promise<void>} Resolves when the task is created or throws an error.
   */
  async function createTask (): Promise<void> {
    try {
      // Validate task description to ensure it's not empty
      if (taskDescription.trim() === '') {
        alert('Please enter a task first!')
        return
      }

      // 1) Encrypt the user's task description
      const { ciphertext } = await walletClient.encrypt({
        plaintext: Utils.toArray(taskDescription, 'utf8'),
        protocolID: [0, 'todo list'],
        keyID: '1'
      })

      // 2) Create a PushDrop output script with the encrypted task
      const pushdrop = new PushDrop(walletClient)
      const lockingScript = await pushdrop.lock(
        [ciphertext], // Token field 0: encrypted task text
        [0, 'todo list'], // Protocol ID
        '1', // Key ID
        'self' // Recipient
      )

      // 3) Form and broadcast the transaction with the task
      const action = await walletClient.createAction({
        description: `Create a new ToDo: ${taskDescription}`,
        outputs: [
          {
            lockingScript: lockingScript.toHex(), // Convert script to hex format
            satoshis: taskValue, // Amount of satoshis to lock
            basket: 'todo tokens', // Categorize output
            outputDescription: 'Create task token' // Output description
          }
        ]
      })

      // Notify user of success and reset input
      alert(`Task Created! TX: ${action.txid ?? 'unknown'}`)
      setTaskDescription('')
    } catch (e: unknown) {
      // Handle and display errors gracefully
      const errorMessage =
        e instanceof Error ? e.message : 'Unknown error occurred'
      console.error('Task creation failed:', e)
      alert(`Error: ${errorMessage}`)
    }
  }

  // Handler for task description input change
  const handleDescriptionChange = (e: ChangeEvent<HTMLInputElement>): void => {
    setTaskDescription(e.target.value)
  }

  // Handler for satoshi value input change
  const handleValueChange = (e: ChangeEvent<HTMLInputElement>): void => {
    setTaskValue(Number(e.target.value))
  }

  // Handler for button click to trigger task creation
  const handleCreateTask = (): void => {
    createTask().catch(e => console.error('Failed to create task:', e))
  }

  return (
    <div style={{ margin: '2rem' }}>
      <h1>My ToDo App</h1>
      <p>
        Create a new to-do item by entering its description and “locking”
        satoshis. You’ll get them back when you redeem the token.
      </p>
      <input
        type='text'
        placeholder='Task to do...'
        value={taskDescription}
        onChange={handleDescriptionChange} // Explicitly typed handler
      />
      <br />
      <br />
      <label>
        Satoshis to lock:
        <input
          type='number'
          min={1}
          value={taskValue}
          onChange={handleValueChange} // Explicitly typed handler
        />
      </label>
      <br />
      <br />
      <button onClick={handleCreateTask}>Create ToDo</button>
    </div>
  )
}
```

### Running Locally

![todo-app-task-payment](https://docs.projectbabbage.com/assets/images/todo-app-task-payment.webp)

From your root dir (todo-bsv), run `npm run start` and visit http://localhost:5173 (check terminal for port). Ensure your wallet is running for signing.

Navigate back with `cd ..` if you’re still in `frontend/`.

![todo-app-task-broadcast](https://docs.projectbabbage.com/assets/images/todo-app-task-broadcast.webp)

Launch your browser and navigate to the designated port. If you have a BRC-100 wallet such as BSV Desktop running, wallet prompts will ask you to grant encryption permission and sign the transaction. Once the payment is finalized, an alert will display your `txid` (transaction ID). Enter this `txid` on [WhatsOnChain.com](https://whatsonchain.com) to inspect your task token on the BSV mainnet blockchain. Paste the `txid` into the WhatsOnChain.com search field to monitor its status as it updates.

## Remaining Concepts

Let's think through how you could extend the app after the create path works.

### Redeeming (Completing) a ToDo

When the user finishes a task, they can *spend* their token to themselves, effectively reclaiming the satoshis. In a real app, you keep track of the outpoint (the “token ID”), BEEF data, and locking script for the selected output, then produce an unlocking script for that exact output.

Do not paste a placeholder redemption block into your first app. Finish the create path first, then implement redemption from the wallet output you selected. The moving pieces are:

1. `listOutputs({ basket: 'todo tokens' })` to find unspent task tokens.
2. Decode the selected output with PushDrop and decrypt the task text.
3. Use `walletClient.createAction` with the selected outpoint as an input.
4. Use the same PushDrop protocol parameters to build the unlocking script.
5. Call `walletClient.signAction` with the unlocking script and show the returned txid.

A minimal shape looks like this:

```tsx
async function redeemTask(tokenOutpoint: string): Promise<void> {
  const { signableTransaction } = await walletClient.createAction({
    description: 'Redeem a ToDo item',
    inputs: [{
      outpoint: tokenOutpoint,
      inputDescription: 'redeem the to-do token',
      unlockingScriptLength: 108
    }]
  })
  if (signableTransaction == null) {
    throw new Error('Wallet did not return a signable transaction')
  }
  // Advanced: use pushdrop.unlock(...) and walletClient.signAction(...)
  // with the selected output's BEEF and locking script.
}
```

So far, you’ve built an app that creates ToDo tokens. Treat redemption as the next advanced exercise after you can reliably list and inspect the wallet outputs created by this tutorial.

---

## Retrieving ToDo Items

So far, you’ve completed the *create* task. If you’d like a list of unredeemed tasks, you can ask the wallet for all outputs in a “basket.”

```ts
const tasks = await walletClient.listOutputs({ basket: 'todo tokens' })
for (const t of tasks.outputs) {
  console.log('Unspent token outpoint =>', t.outpoint)
  const { satoshis } = t
  // Possibly also decipher the pushdrop-encrypted content with `PushDrop.decode()` and `walletClient.decrypt()`
}
```

This is how you can display an overview of tasks on the UI:
1. `listOutputs({ basket: 'todo tokens' })` gives unredeemed tokens.
2. For each, decode with PushDrop and decrypt the embedded “task text.”

## Putting It All Together

At a high level, your final app might look like this:

1. **Initial Load**:
   - `listOutputs({ basket: 'todo tokens' })` to fetch unredeemed tasks.
   - Decrypt each to show the user’s actual “to-do text.”

2. **Create**:
   - The user types a new task.
   - You call `walletClient.encrypt(...)` to encrypt text.
   - You create a PushDrop output (`pushdrop.lock()`, calling `walletClient.createAction(...)` to finalize and broadcast.

3. **Redeem**:
   - The user clicks “Complete,” referencing that token’s `outpoint`.
   - You do `createAction` with the old token as an input with `pushdrop.unlock()`, returning the satoshis to yourself.

4. **Refresh**:
   - Reload or re-call “listOutputs” to see which tasks remain.

This fosters a “tokenized to-do list”: each unredeemed UTXO is a “task,” and completion reclaims your deposit.

## Deploying Your App

Up till now, you've been running your app locally in LARS. But deploying is effortless, since CARS (the Cloud Automated Runtime System) is compatible with all LARS projects out of the box. Just:

- `npm run cars` to open the CARS menu system in your terminal
- Create a new CARS config, and select a hosting provider from the list
- Build an artifact
- Go to "manage project" and top up your hosting balance from BSV Desktop before releasing
- Go to "releases" and "auto-release" the project
- Once successful, you can "Get Project Info" in the project menu to see your frontend URL
- You can also "Set Custom Frontend Domain" and follow the on-screen instructions to point your own website name at your new app

Now you can share your BSV app with users and collaborators! To update it just build another artifact and auto-release again. Your host should email you when your balance gets low or you could even [run your own CARS](https://github.com/p2ppsr/cars-node/blob/master/GETTING_STARTED.md)!

## Where to Next?

- **Overlay Services**: If you want a global *shared* to-do list, you can use an overlay network (topic managers + lookup services). That’s beyond simple wallets, but it’s the next step to let multiple users share “ToDo tokens.”
- **Advanced PushDrop**: Consider storing extra data (like deadlines, priorities) in multiple PushDrop fields.
- **Identity**: Explore the `IdentityClient` to link tasks to your on-chain identity.
- **Contracts**: Use sCrypt to define agreements among people, companies, and even open blockchain bounties claimable by anyone in the world.

## Summary

You’ve created a minimal [**BRC-102**](https://github.com/bitcoin-sv/BRCs/blob/master/apps/0102.md)–structured project:
- **frontend/** for the React app,
- **deployment-info.json** to define local dev configs,
- **WalletClient** usage in your code for encryption, signing, and listing.

From this skeleton, you can spin up bigger UTXO-based token flows for anything: from timed deposit contracts to mini games to complex multi-user overlay services. The BSV blockchain’s stable protocol, combined with the robust [TypeScript SDK](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/sdk) and a user’s [**BRC-100**](https://github.com/bitcoin-sv/BRCs/blob/master/wallet/0100.md) wallet, makes all these flows simpler (and more scalable) than on other blockchains.

**Congratulations!** You have a running local dApp on BSV, complete with tokenization and encryption. Your to-do items truly live as tokens in your wallet. When you’re ready for more advanced features—like multi-user overlays or contract-based logic beyond simple pushdrop—just build on these same fundamentals. Enjoy your new BSV-powered app!
