| name | squads |
| creator | raunit-dev |
| description | Complete guide for Squads Protocol - Solana's leading smart account and multisig infrastructure. Covers Squads V4 Multisig for team treasury management, Smart Account Program for account abstraction and programmable wallets, and Grid for stablecoin rails and fintech infrastructure. |
Squads Protocol Development Guide
Squads Protocol is Solana's premier smart account infrastructure, securing over $10 billion in digital assets. This guide covers all three main products: Squads V4 Multisig, Smart Account Program, and Grid.
Overview
Squads Protocol provides:
- Squads V4 Multisig - Multi-signature wallet for teams with proposals, voting, time locks, spending limits, and program upgrade management
- Smart Account Program - Account abstraction infrastructure with session keys, passkeys, programmable policies, and direct debits
- Grid - Open finance infrastructure for stablecoin rails, neobank platforms, and enterprise payment systems
Program IDs
| Program | Mainnet | Devnet |
|---|
| Squads V4 Multisig | SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf | SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf |
| Smart Account | SMRTzfY6DfH5ik3TKiyLFfXexV8uSG3d2UksSCYdunG | SMRTzfY6DfH5ik3TKiyLFfXexV8uSG3d2UksSCYdunG |
| External Signature (Grid) | ExtSgUPtP3JyKUysFw2S5fpL5fWfUPzGUQLd2bTwftXN | ExtSgUPtP3JyKUysFw2S5fpL5fWfUPzGUQLd2bTwftXN |
Eclipse Mainnet:
| Program | Address |
|---|
| Squads V4 Multisig | eSQDSMLf3qxwHVHeTr9amVAGmZbRLY2rFdSURandt6f |
Quick Start
Installation
npm install @sqds/multisig @solana/web3.js
npm install @sqds/grid
npm install @sqds/grid-react-native
Basic Setup
import * as multisig from "@sqds/multisig";
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed");
const wallet = Keypair.fromSecretKey();
const SQUADS_PROGRAM_ID = new PublicKey("SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf");
Squads V4 Multisig
Squads V4 is the latest version of the multisig protocol, featuring enhanced security, time locks, spending limits, and batch transactions.
Core Concepts
Multisig Account: The main account that holds configuration (members, threshold, time lock settings).
Vault: A PDA controlled by the multisig where assets are stored. Each multisig can have multiple vaults (indexed 0, 1, 2...).
Proposal: A request to execute a transaction that requires approval from multisig members.
Transaction: The actual instruction(s) to be executed once a proposal is approved.
Permission System
Members can have different permissions:
import { Permission, Permissions } from "@sqds/multisig/lib/types";
const fullPermissions = Permissions.all();
const voteOnly = Permissions.fromPermissions([Permission.Vote]);
const initiateAndVote = Permissions.fromPermissions([Permission.Initiate, Permission.Vote]);
const executeOnly = Permissions.fromPermissions([Permission.Execute]);
| Permission | Description |
|---|
Initiate | Can create new proposals |
Vote | Can approve or reject proposals |
Execute | Can execute approved proposals |
Creating a Multisig
import * as multisig from "@sqds/multisig";
const { Permissions } = multisig.types;
const createKey = Keypair.generate();
const [multisigPda] = multisig.getMultisigPda({
createKey: createKey.publicKey,
});
const signature = await multisig.rpc.multisigCreateV2({
connection,
createKey,
creator: wallet,
multisigPda,
configAuthority: null,
threshold: 2,
members: [
{ key: member1.publicKey, permissions: Permissions.all() },
{ key: member2.publicKey, permissions: Permissions.all() },
{ key: member3.publicKey, permissions: Permissions.fromPermissions([Permission.Vote]) },
],
timeLock: 0,
rentCollector: ,
});
.(, multisigPda.());
.(, signature);
Vault Operations
Vaults are PDAs that hold the multisig's assets:
const [vaultPda] = multisig.getVaultPda({
multisigPda,
index: 0,
});
console.log("Vault address:", vaultPda.toString());
const balance = await connection.getBalance(vaultPda);
console.log("Vault balance:", balance / 1e9, "SOL");
Important: Always send funds to the vault PDA, not the multisig account itself.
Creating a Vault Transaction
import { SystemProgram, LAMPORTS_PER_SOL } from "@solana/web3.js";
const multisigAccount = await multisig.accounts.Multisig.fromAccountAddress(
connection,
multisigPda
);
const transactionIndex = BigInt(Number(multisigAccount.transactionIndex) + 1);
const [transactionPda] = multisig.getTransactionPda({
multisigPda,
index: transactionIndex,
});
const transferIx = SystemProgram.transfer({
fromPubkey: vaultPda,
toPubkey: recipientPubkey,
lamports: 0.1 * LAMPORTS_PER_SOL,
});
const signature = await multisig.rpc.vaultTransactionCreate({
connection,
feePayer: wallet,
multisigPda,
transactionIndex,
creator: wallet.publicKey,
vaultIndex: 0,
ephemeralSigners: 0,
transactionMessage: new TransactionMessage({
payerKey: vaultPda,
recentBlockhash: ( connection.()).,
: [transferIx],
}),
});
.(, transactionPda.());
Creating and Managing Proposals
const [proposalPda] = multisig.getProposalPda({
multisigPda,
transactionIndex,
});
const createProposalSig = await multisig.rpc.proposalCreate({
connection,
feePayer: wallet,
multisigPda,
transactionIndex,
creator: wallet,
});
console.log("Proposal created:", proposalPda.toString());
Voting on Proposals
const approveSig = await multisig.rpc.proposalApprove({
connection,
feePayer: wallet,
multisigPda,
transactionIndex,
member: wallet,
});
console.log("Proposal approved");
const rejectSig = await multisig.rpc.proposalReject({
connection,
feePayer: wallet,
multisigPda,
transactionIndex,
member: wallet,
memo: "Reason for rejection",
});
Executing Approved Transactions
const proposal = await multisig.accounts.Proposal.fromAccountAddress(
connection,
proposalPda
);
if (proposal.status.__kind === "Approved") {
const executeSig = await multisig.rpc.vaultTransactionExecute({
connection,
feePayer: wallet,
multisigPda,
transactionIndex,
member: wallet.publicKey,
});
console.log("Transaction executed:", executeSig);
}
Spending Limits
Spending limits allow members to execute transactions up to a certain amount without full multisig approval:
const createSpendingLimitSig = await multisig.rpc.configTransactionCreate({
connection,
feePayer: wallet,
multisigPda,
transactionIndex,
creator: wallet.publicKey,
actions: [{
__kind: "AddSpendingLimit",
createKey: spendingLimitCreateKey.publicKey,
vaultIndex: 0,
mint: SOL_MINT,
amount: BigInt(1 * LAMPORTS_PER_SOL),
period: multisig.types.Period.Day,
members: [trustedMember.publicKey],
destinations: [allowedDestination],
}],
});
const useSpendingLimitSig = await multisig.rpc.spendingLimitUse({
connection,
feePayer: wallet,
multisigPda,
member: trustedMember,
spendingLimit: spendingLimitPda,
mint: SOL_MINT,
vaultIndex: 0,
amount: BigInt(0.5 * LAMPORTS_PER_SOL),
decimals: 9,
: allowedDestination,
});
Batch Transactions
Execute multiple transactions atomically:
const [batchPda] = multisig.getBatchPda({
multisigPda,
batchIndex: transactionIndex,
});
const createBatchSig = await multisig.rpc.batchCreate({
connection,
feePayer: wallet,
multisigPda,
batchIndex: transactionIndex,
creator: wallet,
vaultIndex: 0,
});
await multisig.rpc.batchAddTransaction({
connection,
feePayer: wallet,
multisigPda,
batchIndex: transactionIndex,
transactionIndex: 1,
vaultIndex: 0,
transactionMessage: ,
});
await multisig.rpc.batchAddTransaction({
connection,
feePayer: wallet,
multisigPda,
batchIndex: transactionIndex,
transactionIndex: 2,
vaultIndex: 0,
transactionMessage: ,
});
Config Transactions
Modify multisig settings (requires proposal approval):
const addMemberSig = await multisig.rpc.configTransactionCreate({
connection,
feePayer: wallet,
multisigPda,
transactionIndex,
creator: wallet.publicKey,
actions: [{
__kind: "AddMember",
newMember: {
key: newMemberPubkey,
permissions: Permissions.all(),
},
}],
});
const changeThresholdSig = await multisig.rpc.configTransactionCreate({
connection,
feePayer: wallet,
multisigPda,
transactionIndex,
creator: wallet.publicKey,
actions: [{
__kind: "ChangeThreshold",
newThreshold: 3,
}],
});
const removeMemberSig = await multisig.rpc.configTransactionCreate({
connection,
feePayer: wallet,
multisigPda,
transactionIndex,
creator: wallet.publicKey,
actions: [{
__kind: "RemoveMember",
oldMember: memberToRemove,
}],
});
Time Locks
Add a delay before approved transactions can execute:
const signature = await multisig.rpc.multisigCreateV2({
connection,
createKey,
creator: wallet,
multisigPda,
configAuthority: null,
threshold: 2,
members: [...],
timeLock: 86400,
rentCollector: null,
});
PDA Derivation Reference
import * as multisig from "@sqds/multisig";
const [multisigPda] = multisig.getMultisigPda({
createKey: createKeyPubkey,
});
const [vaultPda] = multisig.getVaultPda({
multisigPda,
index: 0,
});
const [transactionPda] = multisig.getTransactionPda({
multisigPda,
index: transactionIndex,
});
const [proposalPda] = multisig.getProposalPda({
multisigPda,
transactionIndex,
});
const [batchPda] = multisig.getBatchPda({
multisigPda,
batchIndex,
});
const [spendingLimitPda] = multisig.getSpendingLimitPda({
multisigPda,
createKey: spendingLimitCreateKey,
});
const [programConfigPda] = multisig.getProgramConfigPda({});
const [ephemeralSignerPda] = multisig.getEphemeralSignerPda({
transactionPda,
ephemeralSignerIndex: 0,
});
Smart Account Program
The Smart Account Program provides account abstraction features for Solana, enabling programmable wallets with session keys, passkeys, and policy-based execution.
Core Concepts
Smart Account: A programmable wallet controlled by policies rather than just private keys.
Session Key: Temporary keys with limited permissions for specific operations.
Passkey: WebAuthn/FIDO2 authentication using biometrics or hardware keys.
Policy: Rules that govern what transactions can be executed.
Features
- Rent-free Deployment - No rent required for smart accounts
- Atomic Policy Enforcement - All policy checks happen atomically
- Account Compression - Efficient storage for multiple signers
- Extensible Policies - Custom policy programs can be added
- Direct Debits - Automated recurring payments
- Subscriptions - Managed subscription billing
REST API Base URL
https://developer-api.squads.so/api/v1
Creating a Smart Account
const response = await fetch("https://developer-api.squads.so/api/v1/accounts", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`,
},
body: JSON.stringify({
email: "user@example.com",
signer: signerPublicKey,
}),
});
const account = await response.json();
console.log("Smart account:", account.address);
Session Key Management
Session keys allow delegated access with specific permissions:
const sessionResponse = await fetch(
`https://developer-api.squads.so/api/v1/accounts/${accountId}/sessions`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`,
},
body: JSON.stringify({
publicKey: sessionKeyPublicKey,
expiresAt: Date.now() + 24 * 60 * 60 * 1000,
permissions: ["transfer", "swap"],
limits: {
maxAmount: "1000000000",
dailyLimit: "5000000000",
},
}),
}
);
Passkey Authentication
const registerResponse = await fetch(
`https://developer-api.squads.so/api/v1/accounts/${accountId}/passkeys`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`,
},
body: JSON.stringify({
credentialId: webAuthnCredential.id,
publicKey: webAuthnCredential.publicKey,
attestation: webAuthnCredential.attestation,
}),
}
);
const authResponse = await fetch(
"https://developer-api.squads.so/api/v1/auth/passkey",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
credentialId: webAuthnCredential.id,
assertion: webAuthnAssertion,
}),
}
);
Policy Configuration
const policyResponse = await fetch(
`https://developer-api.squads.so/api/v1/accounts/${accountId}/policies`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`,
},
body: JSON.stringify({
type: "spending_limit",
params: {
mint: "So11111111111111111111111111111111111111112",
amount: "10000000000",
period: "daily",
},
}),
}
);
Grid
Grid is Squads' open finance infrastructure for stablecoin rails, enabling neobank platforms and enterprise payment systems.
Features
- Stablecoin Rails - USDC/USDT payment infrastructure
- Programmable Payments - Automated money flows
- Virtual Accounts - Fiat on/off ramps
- KYC Integration - Built-in compliance tools
- Self-Custody - Users maintain control of assets
API Authentication
const otpResponse = await fetch("https://developer-api.squads.so/api/v1/auth/email/otp", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email: "user@example.com",
}),
});
const verifyResponse = await fetch("https://developer-api.squads.so/api/v1/auth/email/verify", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email: "user@example.com",
otp: "123456",
}),
});
const { accessToken } = await verifyResponse.json();
Account Management
const accountResponse = await fetch(
`https://developer-api.squads.so/api/v1/accounts/${accountId}`,
{
headers: {
"Authorization": `Bearer ${accessToken}`,
},
}
);
const account = await accountResponse.json();
console.log("Balance:", account.balance);
console.log("Status:", account.status);
Payment Operations
const paymentResponse = await fetch(
"https://developer-api.squads.so/api/v1/payments",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${accessToken}`,
},
body: JSON.stringify({
amount: "100000000",
currency: "USDC",
recipient: recipientAddress,
memo: "Payment for services",
}),
}
);
const payment = await paymentResponse.json();
console.log("Payment ID:", payment.id);
console.log("Status:", payment.status);
Standing Orders (Recurring Payments)
const standingOrderResponse = await fetch(
"https://developer-api.squads.so/api/v1/standing-orders",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${accessToken}`,
},
body: JSON.stringify({
amount: "50000000",
currency: "USDC",
recipient: recipientAddress,
frequency: "monthly",
startDate: "2024-02-01",
memo: "Monthly subscription",
}),
}
);
Spending Limits
const limitResponse = await fetch(
`https://developer-api.squads.so/api/v1/accounts/${accountId}/limits`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${accessToken}`,
},
body: JSON.stringify({
type: "daily",
amount: "1000000000",
currency: "USDC",
}),
}
);
Best Practices
Security
- Never share private keys - Use environment variables for sensitive data
- Verify program IDs - Always confirm you're interacting with official programs
- Use time locks - For high-value treasuries, add time locks for additional security
- Limit permissions - Give members only the permissions they need
- Test on devnet - Always test transactions on devnet before mainnet
Transaction Optimization
- Use Address Lookup Tables - Reduce transaction size for complex operations
- Batch transactions - Group related operations when possible
- Set appropriate compute budget - Avoid transaction failures due to compute limits
import { ComputeBudgetProgram } from "@solana/web3.js";
const computeBudgetIx = ComputeBudgetProgram.setComputeUnitLimit({
units: 400_000,
});
const priorityFeeIx = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 10_000,
});
Error Handling
try {
const signature = await multisig.rpc.proposalCreate({
connection,
feePayer: wallet,
multisigPda,
transactionIndex,
creator: wallet,
});
} catch (error) {
if (error.message.includes("NotAMember")) {
console.error("Wallet is not a member of this multisig");
} else if (error.message.includes("Unauthorized")) {
console.error("Wallet does not have required permissions");
} else if (error.message.includes("InvalidTransactionIndex")) {
console.error("Transaction index already used");
} else {
throw error;
}
}
Fetching Account State
const multisigAccount = await multisig.accounts.Multisig.fromAccountAddress(
connection,
multisigPda
);
console.log("Threshold:", multisigAccount.threshold);
console.log("Members:", multisigAccount.members);
console.log("Transaction Index:", multisigAccount.transactionIndex.toString());
const proposal = await multisig.accounts.Proposal.fromAccountAddress(
connection,
proposalPda
);
console.log("Status:", proposal.status.__kind);
console.log("Approved:", proposal.approved.length);
console.log("Rejected:", proposal.rejected.length);
Common Patterns
Program Upgrade Multisig
Secure program upgrade authority with a multisig:
const transferAuthIx = createSetAuthorityInstruction(
programDataAddress,
currentAuthority,
AuthorityType.UpgradeAuthority,
vaultPda
);
Treasury Management
const [operationsVault] = multisig.getVaultPda({ multisigPda, index: 0 });
const [reserveVault] = multisig.getVaultPda({ multisigPda, index: 1 });
const [grantVault] = multisig.getVaultPda({ multisigPda, index: 2 });
Validator Operations
Resources
Official Documentation
GitHub Repositories
Security Audits
- OtterSec audit
- Neodyme audit
- Certora audit
- Trail of Bits audit
Verify Program Builds
cargo install solana-verify
solana-verify get-program-hash -u mainnet-beta SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf
Skill Structure
squads/
├── SKILL.md # This file
├── resources/
│ ├── program-addresses.md # All program IDs and PDAs
│ ├── multisig-api-reference.md # @sqds/multisig SDK reference
│ ├── smart-account-api-reference.md # Smart Account API reference
│ └── grid-api-reference.md # Grid REST API reference
├── examples/
│ ├── multisig/
│ │ ├── create-multisig.ts # Create multisig with members
│ │ ├── proposals-voting.ts # Proposals and voting
│ │ ├── vault-transactions.ts # Vault operations
│ │ └── spending-limits.ts # Spending limit management
│ ├── smart-account/
│ │ ├── account-creation.ts # Smart account setup
│ │ └── session-keys.ts # Session key management
│ └── grid/
│ ├── api-quickstart.ts # REST API basics
│ └── payments.ts # Payment operations
├── templates/
│ ├── multisig-setup.ts # Multisig client template
│ ├── smart-account-setup.ts # Smart account template
│ └── grid-client.ts # Grid API client template
└── docs/
└── troubleshooting.md # Common issues and solutions