Skip to main content
This example demonstrates how to make x402 payments using Squads multisig wallets. Multisig wallets require multiple signatures to approve transactions, making them ideal for organizational payments or enhanced security. Source: GitHub › scripts/solana-example/squads-payment.ts

When to Use Squads Multisig

Use Squads when:
  • You need multi-signature approval for payments
  • You’re building organizational or treasury wallets
  • You want shared control over payment authorization
  • You need threshold-based transaction approval (e.g., 2 of 3 signatures)
Use single-sig wallets when:
  • You’re building individual user applications
  • You don’t need multi-party approval
  • You want simpler, faster transaction flows

Full Example

import "dotenv/config";
import { logger, logResponse } from "../logger";
import {
  clusterApiUrl,
  Connection,
  Keypair,
  PublicKey,
  sendAndConfirmTransaction,
  SystemProgram,
  Transaction,
} from "@solana/web3.js";
import * as multisig from "@sqds/multisig";
import fs from "fs";
const { Permission, Permissions } = multisig.types;

import { wrap as wrapFetch } from "@faremeter/fetch";
import { createSquadsWallet } from "@faremeter/wallet-solana-squads";
import { createPaymentHandler } from "@faremeter/x-solana-settlement";

const { PAYER_KEYPAIR_PATH } = process.env;

if (!PAYER_KEYPAIR_PATH) {
  throw new Error("PAYER_KEYPAIR_PATH must be set in your environment");
}

const transferSol = async (
  connection: Connection,
  receiver: PublicKey,
  sender: Keypair,
  amount: number,
) => {
  const transaction = new Transaction().add(
    SystemProgram.transfer({
      fromPubkey: sender.publicKey,
      toPubkey: receiver,
      lamports: amount * 1000000000,
    }),
  );

  await sendAndConfirmTransaction(connection, transaction, [sender]);
};

const network = "devnet";
const connection = new Connection(clusterApiUrl(network), "confirmed");
const keypair = Keypair.fromSecretKey(
  Uint8Array.from(JSON.parse(fs.readFileSync(PAYER_KEYPAIR_PATH, "utf-8"))),
);

async function createSquad() {
  const createKey = Keypair.generate();
  const squadMember = Keypair.generate();

  await transferSol(connection, squadMember.publicKey, keypair, 0.002);
  await transferSol(connection, createKey.publicKey, keypair, 0.002);

  const [multisigPda] = multisig.getMultisigPda({
    createKey: createKey.publicKey,
  });

  const programConfigPda = multisig.getProgramConfigPda({})[0];
  const programConfig =
    await multisig.accounts.ProgramConfig.fromAccountAddress(
      connection,
      programConfigPda,
    );

  const createSquadInstruction = multisig.instructions.multisigCreateV2({
    createKey: createKey.publicKey,
    creator: keypair.publicKey,
    multisigPda,
    configAuthority: null,
    timeLock: 0,
    members: [
      {
        key: keypair.publicKey,
        permissions: Permissions.all(),
      },
      {
        key: squadMember.publicKey,
        permissions: Permissions.fromPermissions([Permission.Vote]),
      },
    ],
    threshold: 2,
    treasury: programConfig.treasury,
    rentCollector: null,
  });

  const transaction = new Transaction().add(createSquadInstruction);
  const squadCreateSignature = await sendAndConfirmTransaction(
    connection,
    transaction,
    [keypair, createKey],
  );

  logger.info(`Created squad with signature: ${squadCreateSignature}`);

  return {
    multisigPda,
    squadMember,
  };
}

const { multisigPda, squadMember } = await createSquad();
const wallet = await createSquadsWallet(
  network,
  connection,
  keypair,
  multisigPda,
  squadMember,
);

const fetchWithPayer = wrapFetch(fetch, {
  handlers: [createPaymentHandler(wallet)],
});

const req = await fetchWithPayer("http://127.0.0.1:3000/protected");
await logResponse(req);

Step-by-Step Breakdown

1. Create Squad Members

const createKey = Keypair.generate();
const squadMember = Keypair.generate();

await transferSol(connection, squadMember.publicKey, keypair, 0.002);
await transferSol(connection, createKey.publicKey, keypair, 0.002);
Create and fund keypairs for:
  • createKey: Used to derive the multisig PDA (address)
  • squadMember: A member of the multisig (needed for approval)
  • Fund them with SOL for transaction fees

2. Derive Multisig Address

const [multisigPda] = multisig.getMultisigPda({
  createKey: createKey.publicKey,
});
The multisig address is deterministically derived from the create key.

3. Create the Multisig

const createSquadInstruction = multisig.instructions.multisigCreateV2({
  createKey: createKey.publicKey,
  creator: keypair.publicKey,
  multisigPda,
  configAuthority: null,
  timeLock: 0,
  members: [
    {
      key: keypair.publicKey,
      permissions: Permissions.all(),
    },
    {
      key: squadMember.publicKey,
      permissions: Permissions.fromPermissions([Permission.Vote]),
    },
  ],
  threshold: 2,
  treasury: programConfig.treasury,
  rentCollector: null,
});
This creates a multisig with:
  • Members: Two members (keypair and squadMember)
  • Threshold: 2 signatures required (both members must approve)
  • Permissions: Creator has all permissions, member can only vote

4. Create Squads Wallet

const wallet = await createSquadsWallet(
  network,
  connection,
  keypair,           // Your admin keypair
  multisigPda,       // The multisig address
  squadMember,       // Member needed for approval
);
The Squads wallet:
  • Creates vault transactions
  • Proposes payments
  • Gathers approvals from members
  • Executes approved transactions

5. Make Payment

const fetchWithPayer = wrapFetch(fetch, {
  handlers: [createPaymentHandler(wallet)],
});

const req = await fetchWithPayer("http://127.0.0.1:3000/protected");
When a payment is required, the wallet:
  1. Creates a vault transaction proposal
  2. Adds both member approvals
  3. Executes the transaction
  4. All steps happen automatically

Multisig Configuration

Members

Each member has permissions:
  • Permissions.all(): Full control (admin)
  • Permission.Vote: Can approve transactions
  • Permission.Execute: Can execute approved transactions

Threshold

The threshold determines how many signatures are needed:
  • threshold: 2 means both members must approve
  • threshold: 1 means only one member needs to approve

Time Lock

Optional delay before execution (set to 0 for immediate execution).

Multisig vs Single-Sig

import { createSquadsWallet } from "@faremeter/wallet-solana-squads";

const wallet = await createSquadsWallet(
  network,
  connection,
  adminKeypair,
  multisigPda,
  memberKeypair,
);

// Requires multiple signatures
// Creates proposals and approvals automatically
const fetchWithPayer = wrapFetch(fetch, {
  handlers: [createPaymentHandler(wallet)],
});
Tradeoffs:
  • Multisig: Enhanced security, organizational control, but more complex setup
  • Single-sig: Simple, fast, but single point of failure

Payment Flow

When a payment is initiated:
  1. Vault Transaction Creation: The wallet creates a transaction in the multisig vault
  2. Proposal Creation: A proposal is created for the transaction
  3. Approvals: Required members approve the proposal (automated in this example)
  4. Execution: The approved transaction is executed
All steps are handled automatically by the Squads wallet adapter.

Environment Variables

  • PAYER_KEYPAIR_PATH: Path to your admin keypair JSON file