> ## Documentation Index
> Fetch the complete documentation index at: https://new-docs.squads.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Build a complete multisig workflow with the Squads TypeScript SDK

Build a complete multisig workflow using the Squads TypeScript SDK. Create a multisig, propose a transaction, vote on it, and execute it.

## Prerequisites

* Node.js installed
* Solana CLI installed ([installation guide](https://solana.com/docs/intro/installation))
* Basic TypeScript knowledge

## Quick example

```typescript theme={null}
import * as multisig from "@sqds/multisig";
import { Connection, Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js";

const { Permission, Permissions } = multisig.types;
const connection = new Connection("http://localhost:8899", "confirmed");
```

## Full example

Find the complete code for this guide [here](https://github.com/Squads-Protocol/v4-examples/blob/main/typescript/main.ts).

<Steps>
  <Step title="Set up your workspace">
    Create a new directory:

    ```bash theme={null}
    mkdir squads_quickstart
    cd squads_quickstart
    ```

    Create `tsconfig.json`:

    ```json theme={null}
    {
      "compilerOptions": {
        "module": "commonjs",
        "target": "es6",
        "esModuleInterop": true
      }
    }
    ```

    Create `package.json`:

    ```json theme={null}
    {
      "scripts": {
        "test": "npx mocha -r ts-node/register 'main.ts' --timeout 10000"
      },
      "dependencies": {
        "@solana/web3.js": "^1.73.0",
        "@sqds/multisig": "^2.1.3"
      },
      "devDependencies": {
        "@types/chai": "^4.3.3",
        "@types/mocha": "^10.0.6",
        "chai": "^4.3.6",
        "mocha": "^10.3.0",
        "ts-mocha": "^10.0.0",
        "typescript": "^4.8.3"
      }
    }
    ```

    Create `main.ts`:

    ```typescript theme={null}
    import * as multisig from "@sqds/multisig";
    import {
      Connection,
      Keypair,
      LAMPORTS_PER_SOL,
      SystemProgram,
      TransactionMessage,
      VersionedTransaction,
      clusterApiUrl,
    } from "@solana/web3.js";

    const { Permission, Permissions } = multisig.types;

    // Use 'https://api.devnet.solana.com' if there are struggles running locally
    const connection = new Connection("http://localhost:8899", "confirmed");
    ```
  </Step>

  <Step title="Create a multisig">
    ```typescript theme={null}
    describe("Interacting with the Squads V4 SDK", () => {
      const creator = Keypair.generate();
      const secondMember = Keypair.generate();
      before(async () => {
        const airdropSignature = await connection.requestAirdrop(
          creator.publicKey,
          1 * LAMPORTS_PER_SOL
        );
        await connection.confirmTransaction(airdropSignature);
      });

      const createKey = Keypair.generate();

      // Derive the multisig account PDA
      const [multisigPda] = multisig.getMultisigPda({
        createKey: createKey.publicKey,
      });

      it("Create a new multisig", async () => {
        const programConfigPda = multisig.getProgramConfigPda({})[0];

        console.log("Program Config PDA: ", programConfigPda.toBase58());

        const programConfig =
          await multisig.accounts.ProgramConfig.fromAccountAddress(
            connection,
            programConfigPda
          );

        const configTreasury = programConfig.treasury;

        // Create the multisig
        const signature = await multisig.rpc.multisigCreateV2({
          connection,
          // One time random Key
          createKey,
          // The creator & fee payer
          creator,
          multisigPda,
          configAuthority: null,
          timeLock: 0,
          members: [
            {
              key: creator.publicKey,
              permissions: Permissions.all(),
            },
            {
              key: secondMember.publicKey,
              // This permission means that the user will only be able to vote on transactions
              permissions: Permissions.fromPermissions([Permission.Vote]),
            },
          ],
          // This means that there needs to be 2 votes for a transaction proposal to be approved
          threshold: 2,
          rentCollector: null,
          treasury: configTreasury,
          sendOptions: { skipPreflight: true },
        });
        await connection.confirmTransaction(signature);
        console.log("Multisig created: ", signature);
      });
    ```
  </Step>

  <Step title="Create a transaction proposal">
    Create a transaction proposal to send 0.1 SOL from the multisig vault to the creator.

    ```typescript theme={null}
      it("Create a transaction proposal", async () => {
        const [vaultPda] = multisig.getVaultPda({
          multisigPda,
          index: 0,
        });
        // Fund the vault so it can pay for the transfer
        const fundVaultTx = new VersionedTransaction(
          new TransactionMessage({
            payerKey: creator.publicKey,
            recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
            instructions: [
              SystemProgram.transfer({
                fromPubkey: creator.publicKey,
                toPubkey: vaultPda,
                lamports: 0.2 * LAMPORTS_PER_SOL,
              }),
            ],
          }).compileToV0Message()
        );
        fundVaultTx.sign([creator]);
        const fundSignature = await connection.sendTransaction(fundVaultTx);
        await connection.confirmTransaction(fundSignature);
        const instruction = SystemProgram.transfer({
          // The transfer is being signed from the Squads Vault, that is why we use the VaultPda
          fromPubkey: vaultPda,
          toPubkey: creator.publicKey,
          lamports: 0.1 * LAMPORTS_PER_SOL,
        });
        // This message contains the instructions that the transaction is going to execute
        const transferMessage = new TransactionMessage({
          payerKey: vaultPda,
          recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
          instructions: [instruction],
        });

        // Get the current multisig transaction index
        const multisigInfo = await multisig.accounts.Multisig.fromAccountAddress(
          connection,
          multisigPda
        );

        const currentTransactionIndex = Number(multisigInfo.transactionIndex);

        const newTransactionIndex = BigInt(currentTransactionIndex + 1);

        const signature1 = await multisig.rpc.vaultTransactionCreate({
          connection,
          feePayer: creator,
          multisigPda,
          transactionIndex: newTransactionIndex,
          creator: creator.publicKey,
          vaultIndex: 0,
          ephemeralSigners: 0,
          transactionMessage: transferMessage,
          memo: "Transfer 0.1 SOL to creator",
        });

        await connection.confirmTransaction(signature1);

        console.log("Transaction created: ", signature1);

        const signature2 = await multisig.rpc.proposalCreate({
          connection,
          feePayer: creator,
          multisigPda,
          transactionIndex: newTransactionIndex,
          creator,
        });

        await connection.confirmTransaction(signature2);

        console.log("Transaction proposal created: ", signature2);
      });
    ```
  </Step>

  <Step title="Vote on the proposal">
    ```typescript theme={null}
      it("Vote on the created proposal", async () => {
        const transactionIndex =
          await multisig.accounts.Multisig.fromAccountAddress(
            connection,
            multisigPda
          ).then((info) => Number(info.transactionIndex));

        const signature1 = await multisig.rpc.proposalApprove({
          connection,
          feePayer: creator,
          multisigPda,
          transactionIndex: BigInt(transactionIndex),
          member: creator,
        });

        await connection.confirmTransaction(signature1);

        const signature2 = await multisig.rpc.proposalApprove({
          connection,
          feePayer: creator,
          multisigPda,
          transactionIndex: BigInt(transactionIndex),
          member: secondMember,
        });

        await connection.confirmTransaction(signature2);
      });
    ```
  </Step>

  <Step title="Execute the transaction">
    ```typescript theme={null}
      it("Execute the proposal", async () => {
        const transactionIndex =
          await multisig.accounts.Multisig.fromAccountAddress(
            connection,
            multisigPda
          ).then((info) => Number(info.transactionIndex));

        const [proposalPda] = multisig.getProposalPda({
          multisigPda,
          transactionIndex: BigInt(transactionIndex),
        });
        const signature = await multisig.rpc.vaultTransactionExecute({
          connection,
          feePayer: creator,
          multisigPda,
          transactionIndex: BigInt(transactionIndex),
          member: creator.publicKey,
          signers: [creator],
          sendOptions: { skipPreflight: true },
        });

        await connection.confirmTransaction(signature);
        console.log("Transaction executed: ", signature);
      });

    // Don't forget to close the describe block here
    });
    ```
  </Step>

  <Step title="Start a local validator">
    Start a local validator with the Squads V4 program preloaded:

    ```bash theme={null}
    solana-test-validator --url m --clone-upgradeable-program SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf -c BSTq9w3kZwNwpBXJEvTZz2G9ZTNyKBvoSeXMvwb4cNZr -c Fy3YMJCvwbAXUgUM5b91ucUVA3jYzwWLHL3MwBqKsh8n
    ```
  </Step>

  <Step title="Run the script">
    ```bash theme={null}
    yarn test
    ```
  </Step>
</Steps>

## Visualize transactions

View transactions on [Solana Explorer](https://explorer.solana.com/?cluster=custom\&customUrl=http%3A%2F%2Flocalhost%3A8899) by pasting the signature and selecting the appropriate cluster endpoint.

<Accordion title="Using devnet instead of local validator">
  If local validator issues occur, switch to devnet by updating the connection:

  ```typescript theme={null}
  const connection = new Connection("https://api.devnet.solana.com", "confirmed");
  ```

  For devnet rate limit workarounds:

  ```typescript theme={null}
  // Add these two dependencies
  import { readFileSync } from "fs";
  import path from "path";
  import dotenv from "dotenv";

  dotenv.config();

  // Add this before you define `describe`
  const keyFileContents = JSON.parse(
    readFileSync(path.join(process.env.HOME, ".config/solana/id.json")).toString()
  );

  const creator = Keypair.fromSecretKey(new Uint8Array(keyFileContents));
  const secondMember = Keypair.generate();
  ```

  Add this at the beginning of your first test:

  ```typescript theme={null}
  try {
      // Attempt an airdrop
      await connection.requestAirdrop(
        secondMember.publicKey,
        1 * LAMPORTS_PER_SOL
      );
  } catch (e) {
      // If it fails due to rate limits, send from the filesystem keypair
      console.log("airdrop failed");

      const tx = new VersionedTransaction(
        new TransactionMessage({
          payerKey: creator.publicKey,
          recentBlockhash: await (
            await connection.getLatestBlockhash()
          ).blockhash,
          instructions: [
            SystemProgram.transfer({
              fromPubkey: creator.publicKey,
              toPubkey: secondMember.publicKey,
              lamports: 1_000_000,
            }),
          ],
        }).compileToV0Message()
      );

      tx.sign([creator]);

      console.log("Sending SOL...");
      await connection.sendTransaction(tx);
      console.log("SOL sent.");
  }
  ```
</Accordion>

<Accordion title="Troubleshooting">
  | Issue | Solution |
  | - | - |
  | "fetch failed" | Ensure local validator is running |
  | ESM resolution issues | Try SDK versions `2.1.1` or `2.1.0` |
  | Node.js errors | Use Node.js version 22.x or above |
</Accordion>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.