# Start a new dapp

## Getting Started with Typink

Typink is a React hooks library for building dApps that interact with ink! and Solidity smart contracts on Polkadot blockchains. With Typink, you get a **unified developer experience** - the same hooks and APIs work seamlessly across ink! v5 (WASM), ink! v6 (PolkaVM), and Solidity contracts.

### Create a New Project

Start by creating a new Typink project using the interactive CLI:

{% tabs %}
{% tab title="pnpm" %}

```sh
pnpm create typink@latest
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn create typink@latest
```

{% endtab %}

{% tab title="bun" %}

```sh
bunx create typink@latest
```

{% endtab %}

{% tab title="npm" %}

```sh
npm create typink@latest
```

{% endtab %}

{% tab title="npx" %}

```sh
npx create-typink@latest
```

{% endtab %}
{% endtabs %}

The CLI will guide you through an interactive setup:

#### 1. Enter Project Name

```
? Project name: my-typink-dapp
```

#### 2. Select Contract Type

Choose the type of contracts you'll be working with:

```
? Select contract type:
❯ Ink! v6 (PolkaVM, pallet-revive)
  Ink! v6 using Sol ABI (PolkaVM, pallet-revive)
  Solidity (PolkaVM, pallet-revive)
  Ink! v5 (WASM, pallet-contracts)
```

**Contract Types:**

* **Ink! v6** - Latest ink! version on PolkaVM (pallet-revive)
* **Ink! v6 Sol ABI** - ink! v6 with Solidity-style ABI
* **Solidity** - Solidity smart contracts on PolkaVM
* **Ink! v5** - Legacy ink! on WASM (pallet-contracts)

#### 3. Select Networks

Choose one or more networks for your dApp:

```
? Select supported networks: (Press <space> to select)
❯◉ Pop Testnet
 ◯ Passet Hub
```

Available networks depend on your contract type:

* **pallet-contracts** (Ink! v5): Pop Testnet, Aleph Zero Testnet, Aleph Zero, Astar
* **pallet-revive** (Ink! v6/Solidity): Pop Testnet, Passet Hub

### Start Development

Navigate to your project and start the development server:

```bash
cd my-typink-dapp

# npm
npm run dev

# pnpm
pnpm dev

# yarn
yarn dev

# bun
bun dev
```

Open <http://localhost:3000> to see your dApp running!

### Project Structure

Your project follows Next.js 15 App Router structure:

```
my-typink-dapp/
├── src/
│   ├── app/                    # Next.js app directory
│   │   ├── layout.tsx         # Root layout
│   │   ├── page.tsx           # Home page
│   │   └── globals.css        # Global styles
│   ├── components/            # React components
│   │   ├── shared/           # Shared UI components
│   │   └── [contract]-board.tsx  # Contract interaction components
│   ├── contracts/            # Contract-related files
│   │   ├── deployments.ts    # Contract deployment addresses
│   │   └── types/           # Generated TypeScript bindings
│   ├── lib/                  # Utility functions
│   │   └── utils.ts
│   └── providers/            # React providers
│       └── app-provider.tsx  # TypinkProvider configuration
├── public/                   # Static assets
├── package.json
├── tsconfig.json
└── next.config.ts
```

#### Key Files

**`src/providers/app-provider.tsx`** - Configures TypinkProvider with networks, wallets, and contracts:

```tsx
import { TypinkProvider, setupTxToaster, SonnerAdapter } from 'typink';
import { deployments } from '@/contracts/deployments';

setupTxToaster({
  adapter: new SonnerAdapter(toast),
});

export function AppProvider({ children }) {
  return (
    <TypinkProvider
      appName="My Typink dApp"
      deployments={deployments}
      supportedNetworks={[popTestnet]}
      defaultNetworkId={popTestnet.id}
      wallets={[subwallet, talisman, polkadotjs]}>
      {children}
    </TypinkProvider>
  );
}
```

**`src/contracts/deployments.ts`** - Registers contract deployments:

```tsx
export enum ContractId {
  FLIPPER = 'flipper',
}

export const deployments: ContractDeployment[] = [
  {
    id: ContractId.FLIPPER,
    metadata: flipperMetadata,
    address: '0x3ddc397c0350cbfb89d4f28d476073d6051067c4',
    network: popTestnet.id,
  },
];
```

### Explore the Example

Your project includes a pre-deployed example contract with working interactions:

1. **Connect Wallet** - Click "Connect Wallet" and select SubWallet, Talisman, or PolkadotJS
2. **View State** - See the current contract state (e.g., Flipper value, Storage value)
3. **Send Transactions** - Interact with the contract (e.g., flip the boolean, set storage)
4. **Watch Progress** - Transaction toasts show real-time progress

#### Example Contracts

* **Ink! v5**: Greeter contract (set and get messages)
* **Ink! v6**: Flipper contract (flip boolean value)
* **Ink! v6 Sol ABI**: Flipper contract with Solidity-style ABI
* **Solidity**: Storage contract (set and get uint256 value)

### Add Your Own Contracts

#### 1. Deploy Your Contract

Deploy your contract to a supported network using:

* [POP CLI](https://learn.onpop.io/contracts/guides/deploy) - Command-line tool for deploying to Pop Network
* [Contracts UI](https://ui.use.ink/) - User-friendly web interface
* [cargo-contract](https://github.com/paritytech/cargo-contract) - CLI tool
* [Remix IDE](https://remix.polkadot.io/) - For Solidity contracts on PolkaVM

You'll receive a contract address after successful deployment.

#### 2. Generate TypeScript Bindings

The project includes a pre-configured typegen script (`./scripts/typegen.ts`) that generates type-safe bindings for all contracts in `src/contracts/artifacts/`.

First, place your contract metadata/ABI files in `src/contracts/artifacts/`, then run:

```bash
# npm
npm run typegen

# pnpm
pnpm typegen

# yarn
yarn typegen

# bun
bun typegen
```

This script automatically processes all metadata/ABI files in `src/contracts/artifacts/` and generates TypeScript bindings to `src/contracts/types/`. The generated TypeScript API (e.g., `MyContractApi`) will be available in `src/contracts/types/my_contract/`.

#### 3. Register Contract Deployment

Add your contract to `src/contracts/deployments.ts`:

```tsx
import myContractMetadata from './artifacts/my_contract/metadata.json';

export enum ContractId {
  FLIPPER = 'flipper',
  MY_CONTRACT = 'my-contract', // Add your contract
}

export const deployments: ContractDeployment[] = [
  // ... existing deployments
  {
    id: ContractId.MY_CONTRACT,
    metadata: myContractMetadata,
    address: 'YOUR_CONTRACT_ADDRESS',
    network: popTestnet.id,
  },
];
```

### Unified Hooks - Works with All Contract Types

Typink's hooks provide a **unified API** that works identically across ink! v5, ink! v6, and Solidity contracts.

#### useContract - Initialize Contract Instance

Get a typed contract instance:

```tsx
import { useContract } from 'typink';
import { MyContractApi } from '@/contracts/types/my_contract';

function MyComponent() {
  const { contract } = useContract<MyContractApi>(ContractId.MY_CONTRACT);

  // contract is now fully typed based on your contract ABI
}
```

#### useContractQuery - Query Contract State

Read contract state with automatic type inference:

**Ink! v6 Flipper Example:**

```tsx
import { useContract, useContractQuery } from 'typink';
import { FlipperContractApi } from '@/contracts/types/flipper';

function FlipperQuery() {
  const { contract } = useContract<FlipperContractApi>(ContractId.FLIPPER);

  const { data: value, isLoading, refresh } = useContractQuery({
    contract,
    fn: 'get', // ✅ Fully typed - autocomplete works!
  });

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <p>Current value: {value?.toString()}</p>
      <button onClick={refresh}>Refresh</button>
    </div>
  );
}
```

**Solidity Storage Example:**

```tsx
import { useContract, useContractQuery } from 'typink';
import { StorageContractApi } from '@/contracts/types/storage';

function StorageQuery() {
  const { contract } = useContract<StorageContractApi>(ContractId.STORAGE);

  const { data: value, isLoading } = useContractQuery({
    contract,
    fn: 'retrieve', // ✅ Same API, different contract!
  });

  return <div>Stored value: {value?.toString()}</div>;
}
```

**💡 The hooks are identical!** Only the contract type and method names change.

#### useContractTx - Send Transactions

Execute contract transactions with the same API:

**Ink! v6 Flipper Example:**

```tsx
import { useContract, useContractTx, txToaster } from 'typink';

function FlipperTx() {
  const { contract } = useContract<FlipperContractApi>(ContractId.FLIPPER);
  const flipTx = useContractTx(contract, 'flip');

  const handleFlip = async () => {
    const toaster = txToaster('Flipping value...');

    try {
      await flipTx.signAndSend({
        callback: (result) => {
          toaster.onTxProgress(result);
        },
      });
    } catch (error) {
      toaster.onTxError(error);
    }
  };

  return (
    <button
      onClick={handleFlip}
      disabled={flipTx.inBestBlockProgress}>
      {flipTx.inBestBlockProgress ? 'Flipping...' : 'Flip'}
    </button>
  );
}
```

**Solidity Storage Example:**

```tsx
import { useContract, useContractTx, txToaster } from 'typink';

function StorageTx() {
  const { contract } = useContract<StorageContractApi>(ContractId.STORAGE);
  const storeTx = useContractTx(contract, 'store');
  const [value, setValue] = useState('');

  const handleStore = async () => {
    const toaster = txToaster('Storing value...');

    try {
      await storeTx.signAndSend({
        args: [BigInt(value)], // ✅ Type-safe args!
        callback: (result) => {
          toaster.onTxProgress(result);
        },
      });
    } catch (error) {
      toaster.onTxError(error);
    }
  };

  return (
    <>
      <input
        value={value}
        onChange={(e) => setValue(e.target.value)}
        type="number"
      />
      <button onClick={handleStore}>Store</button>
    </>
  );
}
```

**💡 Identical hook usage!** The only difference is the contract type and arguments.

#### useWatchContractEvent - Listen to Events

Watch for contract events with type-safe event data:

**Ink! v6 Example:**

```tsx
import { useContract, useWatchContractEvent } from 'typink';

function FlipperEvents() {
  const { contract } = useContract<FlipperContractApi>(ContractId.FLIPPER);
  const [events, setEvents] = useState([]);

  useWatchContractEvent(
    contract,
    'Flipped', // ✅ Event name is typed!
    useCallback((newEvents) => {
      newEvents.forEach((event) => {
        const { name, data } = event;
        console.log(`${name} event:`, data); // ✅ data is typed!
        setEvents((prev) => [...prev, event]);
      });
    }, [])
  );

  return (
    <div>
      <h3>Recent Events:</h3>
      {events.map((event, i) => (
        <div key={i}>{event.name}</div>
      ))}
    </div>
  );
}
```

**💡 Works the same for Solidity contracts!** Just use your Solidity contract's event names.

#### useDeployerTx - Deploy New Contracts

Deploy new contract instances:

```tsx
import { useDeployer, useDeployerTx, txToaster } from 'typink';
import { generateRandomHex } from 'dedot/utils';
import metadata from '@/contracts/types/flipper/metadata.json';

function DeployFlipper() {
  const wasm = metadata.source.wasm; // or metadata.source.hash
  const { deployer } = useDeployer<FlipperContractApi>(metadata, wasm);
  const newFlipperTx = useDeployerTx(deployer, 'new');

  const handleDeploy = async () => {
    const toaster = txToaster('Deploying contract...');

    try {
      await newFlipperTx.signAndSend({
        args: [false], // Initial value
        txOptions: { salt: generateRandomHex() },
        callback: (result, deployedAddress) => {
          toaster.onTxProgress(result);

          if (deployedAddress) {
            console.log('Deployed at:', deployedAddress);
          }
        },
      });
    } catch (error) {
      toaster.onTxError(error);
    }
  };

  return <button onClick={handleDeploy}>Deploy New Flipper</button>;
}
```

### Setup Transaction Toaster

Before using `txToaster()`, configure the global adapter in your app provider:

```tsx
import { setupTxToaster, SonnerAdapter } from 'typink';
import { toast } from 'sonner';

// Setup once at app initialization
setupTxToaster({
  adapter: new SonnerAdapter(toast),
  initialMessage: 'Signing transaction...',
  autoCloseDelay: 5000,
});
```

**Supported toast libraries:**

* **Sonner** (recommended) - `SonnerAdapter`
* **React-Toastify** - `ReactToastifyAdapter`
* **React-Hot-Toast** - `ReactHotToastAdapter`

See the txToaster documentation for more details.

### Complete Example

Here's a complete component showing query, transaction, and events:

```tsx
'use client';

import { useCallback, useState } from 'react';
import { useContract, useContractQuery, useContractTx, useWatchContractEvent, txToaster } from 'typink';
import { FlipperContractApi } from '@/contracts/types/flipper';

export function FlipperBoard() {
  const { contract } = useContract<FlipperContractApi>(ContractId.FLIPPER);
  const [events, setEvents] = useState<string[]>([]);

  // Query current value
  const { data: value, isLoading, refresh } = useContractQuery({
    contract,
    fn: 'get',
  });

  // Transaction to flip value
  const flipTx = useContractTx(contract, 'flip');

  // Watch for Flipped events
  useWatchContractEvent(
    contract,
    'Flipped',
    useCallback((newEvents) => {
      newEvents.forEach((event) => {
        setEvents((prev) => [...prev, `Flipped to: ${event.data.newValue}`]);
      });
    }, [])
  );

  const handleFlip = async () => {
    const toaster = txToaster('Flipping value...');

    try {
      await flipTx.signAndSend({
        callback: (result) => {
          toaster.onTxProgress(result);

          if (result.status.type === 'BestChainBlockIncluded' && !result.dispatchError) {
            refresh(); // Refresh query after success
          }
        },
      });
    } catch (error) {
      toaster.onTxError(error);
    }
  };

  return (
    <div>
      <h2>Flipper Contract</h2>

      {/* Display current value */}
      <div>
        <p>Current Value:</p>
        {isLoading ? (
          <span>Loading...</span>
        ) : (
          <span>{value?.toString()}</span>
        )}
      </div>

      {/* Flip button */}
      <button
        onClick={handleFlip}
        disabled={flipTx.inBestBlockProgress}>
        {flipTx.inBestBlockProgress ? 'Flipping...' : 'Flip Value'}
      </button>

      {/* Recent events */}
      <div>
        <h3>Recent Events:</h3>
        {events.map((event, i) => (
          <div key={i}>{event}</div>
        ))}
      </div>
    </div>
  );
}
```

**💡 This exact same pattern works for Solidity contracts!** Just change the contract type and method names.

***

**Happy building with Typink! 🎉**


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.dedot.dev/typink/getting-started/start-a-new-dapp.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
