Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
.claude/skills/internet-court-squads/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 141% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 213% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 143% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 60% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 192% | 0% |
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.
Squads Protocol provides:
| 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 |
bash# Squads V4 Multisig SDK npm install @sqds/multisig @solana/web3.js # Grid SDK npm install @sqds/grid # Grid React Native SDK npm install @sqds/grid-react-native
typescriptimport * as multisig from "@sqds/multisig"; import { Connection, Keypair, PublicKey } from "@solana/web3.js"; // Setup connection const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed"); // Load wallet const wallet = Keypair.fromSecretKey(/* your secret key */); // Program ID constant const SQUADS_PROGRAM_ID = new PublicKey("SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf");
Squads V4 is the latest version of the multisig protocol, featuring enhanced security, time locks, spending limits, and batch transactions.
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.
Members can have different permissions:
typescriptimport { Permission, Permissions } from "@sqds/multisig/lib/types"; // All permissions (can initiate, vote, and execute) const fullPermissions = Permissions.all(); // Specific permissions 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 |
typescriptimport * as multisig from "@sqds/multisig"; const { Permissions } = multisig.types; // Generate a unique create key (one-time use) const createKey = Keypair.generate(); // Derive the multisig PDA const [multisigPda] = multisig.getMultisigPda({ createKey: createKey.publicKey, }); // Create a 2-of-3 multisig const signature = await multisig.rpc.multisigCreateV2({ connection, createKey, creator: wallet, multisigPda, configAuthority: null, // Immutable config 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, // No time lock (in seconds) rentCollector: null, }); console.log("Multisig created:", multisigPda.toString()); console.log("Transaction:", signature);
Vaults are PDAs that hold the multisig's assets:
typescript// Derive vault PDA (index 0 is the default vault) const [vaultPda] = multisig.getVaultPda({ multisigPda, index: 0, }); console.log("Vault address:", vaultPda.toString()); // Check vault balance 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.
typescriptimport { SystemProgram, LAMPORTS_PER_SOL } from "@solana/web3.js"; // Get current transaction index const multisigAccount = await multisig.accounts.Multisig.fromAccountAddress( connection, multisigPda ); const transactionIndex = BigInt(Number(multisigAccount.transactionIndex) + 1); // Derive transaction PDA const [transactionPda] = multisig.getTransactionPda({ multisigPda, index: transactionIndex, }); // Create a transfer instruction const transferIx = SystemProgram.transfer({ fromPubkey: vaultPda, toPubkey: recipientPubkey, lamports: 0.1 * LAMPORTS_PER_SOL, }); // Create the vault transaction const signature = await multisig.rpc.vaultTransactionCreate({ connection, feePayer: wallet, multisigPda, transactionIndex, creator: wallet.publicKey, vaultIndex: 0, ephemeralSigners: 0, transactionMessage: new TransactionMessage({ payerKey: vaultPda, recentBlockhash: (await connection.getLatestBlockhash()).blockhash, instructions: [transferIx], }), }); console.log("Vault transaction created:", transactionPda.toString());
typescript// Derive proposal PDA const [proposalPda] = multisig.getProposalPda({ multisigPda, transactionIndex, }); // Create proposal for the transaction const createProposalSig = await multisig.rpc.proposalCreate({ connection, feePayer: wallet, multisigPda, transactionIndex, creator: wallet, }); console.log("Proposal created:", proposalPda.toString());
typescript// Approve the proposal const approveSig = await multisig.rpc.proposalApprove({ connection, feePayer: wallet, multisigPda, transactionIndex, member: wallet, }); console.log("Proposal approved"); // Or reject the proposal const rejectSig = await multisig.rpc.proposalReject({ connection, feePayer: wallet, multisigPda, transactionIndex, member: wallet, memo: "Reason for rejection", });
typescript// Check if proposal is approved and ready const proposal = await multisig.accounts.Proposal.fromAccountAddress( connection, proposalPda ); if (proposal.status.__kind === "Approved") { // Execute the vault transaction const executeSig = await multisig.rpc.vaultTransactionExecute({ connection, feePayer: wallet, multisigPda, transactionIndex, member: wallet.publicKey, }); console.log("Transaction executed:", executeSig); }
Spending limits allow members to execute transactions up to a certain amount without full multisig approval:
typescript// Create a spending limit 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, // or token mint amount: BigInt(1 * LAMPORTS_PER_SOL), // 1 SOL period: multisig.types.Period.Day, members: [trustedMember.publicKey], destinations: [allowedDestination], }], }); // Use spending limit (no proposal needed) 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, destination: allowedDestination, });
Execute multiple transactions atomically:
typescript// Create a batch const [batchPda] = multisig.getBatchPda({ multisigPda, batchIndex: transactionIndex, }); const createBatchSig = await multisig.rpc.batchCreate({ connection, feePayer: wallet, multisigPda, batchIndex: transactionIndex, creator: wallet, vaultIndex: 0, }); // Add transactions to the batch await multisig.rpc.batchAddTransaction({ connection, feePayer: wallet, multisigPda, batchIndex: transactionIndex, transactionIndex: 1, vaultIndex: 0, transactionMessage: /* first transaction */, }); await multisig.rpc.batchAddTransaction({ connection, feePayer: wallet, multisigPda, batchIndex: transactionIndex, transactionIndex: 2, vaultIndex: 0, transactionMessage: /* second transaction */, }); // Create proposal and execute as usual
Modify multisig settings (requires proposal approval):
typescript// Add a new member const addMemberSig = await multisig.rpc.configTransactionCreate({ connection, feePayer: wallet, multisigPda, transactionIndex, creator: wallet.publicKey, actions: [{ __kind: "AddMember", newMember: { key: newMemberPubkey, permissions: Permissions.all(), }, }], }); // Change threshold const changeThresholdSig = await multisig.rpc.configTransactionCreate({ connection, feePayer: wallet, multisigPda, transactionIndex, creator: wallet.publicKey, actions: [{ __kind: "ChangeThreshold", newThreshold: 3, }], }); // Remove a member const removeMemberSig = await multisig.rpc.configTransactionCreate({ connection, feePayer: wallet, multisigPda, transactionIndex, creator: wallet.publicKey, actions: [{ __kind: "RemoveMember", oldMember: memberToRemove, }], });
Add a delay before approved transactions can execute:
typescript// Create multisig with time lock (1 day = 86400 seconds) const signature = await multisig.rpc.multisigCreateV2({ connection, createKey, creator: wallet, multisigPda, configAuthority: null, threshold: 2, members: [...], timeLock: 86400, // 1 day in seconds rentCollector: null, });
typescriptimport * as multisig from "@sqds/multisig"; // Multisig PDA const [multisigPda] = multisig.getMultisigPda({ createKey: createKeyPubkey, }); // Vault PDA const [vaultPda] = multisig.getVaultPda({ multisigPda, index: 0, // vault index }); // Transaction PDA const [transactionPda] = multisig.getTransactionPda({ multisigPda, index: transactionIndex, }); // Proposal PDA const [proposalPda] = multisig.getProposalPda({ multisigPda, transactionIndex, }); // Batch PDA const [batchPda] = multisig.getBatchPda({ multisigPda, batchIndex, }); // Spending Limit PDA const [spendingLimitPda] = multisig.getSpendingLimitPda({ multisigPda, createKey: spendingLimitCreateKey, }); // Program Config PDA const [programConfigPda] = multisig.getProgramConfigPda({}); // Ephemeral Signer PDA (for CPI calls) const [ephemeralSignerPda] = multisig.getEphemeralSignerPda({ transactionPda, ephemeralSignerIndex: 0, });
The Smart Account Program provides account abstraction features for Solana, enabling programmable wallets with session keys, passkeys, and policy-based execution.
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.
https://developer-api.squads.so/api/v1typescript// Via REST API 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", // or signer: signerPublicKey, }), }); const account = await response.json(); console.log("Smart account:", account.address);
Session keys allow delegated access with specific permissions:
typescript// Create a session key 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, // 24 hours permissions: ["transfer", "swap"], limits: { maxAmount: "1000000000", // 1 SOL in lamports dailyLimit: "5000000000", // 5 SOL daily }, }), } );
typescript// Register a passkey 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, }), } ); // Authenticate with passkey 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, }), } );
typescript// Set spending policy 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", // SOL amount: "10000000000", // 10 SOL period: "daily", }, }), } );
Grid is Squads' open finance infrastructure for stablecoin rails, enabling neobank platforms and enterprise payment systems.
typescript// Email OTP 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", }), }); // Verify OTP 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();
typescript// Get account details 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);
typescript// Create a payment intent 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", // 100 USDC (6 decimals) currency: "USDC", recipient: recipientAddress, memo: "Payment for services", }), } ); const payment = await paymentResponse.json(); console.log("Payment ID:", payment.id); console.log("Status:", payment.status);
typescript// Create a standing order 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", // 50 USDC currency: "USDC", recipient: recipientAddress, frequency: "monthly", startDate: "2024-02-01", memo: "Monthly subscription", }), } );
typescript// Set spending limit 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", // 1000 USDC currency: "USDC", }), } );
typescriptimport { ComputeBudgetProgram } from "@solana/web3.js"; // Add compute budget instruction const computeBudgetIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000, }); // Add priority fee for faster inclusion const priorityFeeIx = ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 10_000, });
typescripttry { 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; } }
typescript// Get multisig account 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()); // Get proposal status 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);
Secure program upgrade authority with a multisig:
typescript// Transfer upgrade authority to multisig vault const transferAuthIx = createSetAuthorityInstruction( programDataAddress, currentAuthority, AuthorityType.UpgradeAuthority, vaultPda ); // Execute via multisig proposal // Now program upgrades require multisig approval
typescript// Create multiple vaults for different purposes const [operationsVault] = multisig.getVaultPda({ multisigPda, index: 0 }); const [reserveVault] = multisig.getVaultPda({ multisigPda, index: 1 }); const [grantVault] = multisig.getVaultPda({ multisigPda, index: 2 }); // Set spending limits for operations vault // Reserve vault requires full multisig approval
typescript// Manage validator identity and vote account with multisig // Transfer validator identity to vault // Set up spending limits for operational costs
bash# Install solana-verify cargo install solana-verify # Verify Squads V4 program solana-verify get-program-hash -u mainnet-beta SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf
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| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 22,883 | 17,250 | -25% | 1 | 1 | 0% | 3,800 | 9,170 | +141% | 0 | 0 | — |
case-02 | fail→pass | 22,822 | 21,528 | -6% | 1 | 1 | 0% | 3,254 | 10,173 | +213% | 0 | 0 | — |
case-03 | pass→fail | 23,693 | 19,224 | -19% | 1 | 1 | 0% | 3,846 | 9,640 | +151% | 0 | 0 | — |
case-04 | pass→pass | 18,229 | 17,069 | -6% | 1 | 1 | 0% | 2,472 | 8,716 | +253% | 0 | 0 | — |
case-05 | pass→pass | 21,380 | 20,354 | -5% | 1 | 1 | 0% | 3,375 | 10,096 | +199% | 0 | 0 | — |
case-06 | fail→pass | 19,483 | 7,839 | -60% | 1 | 1 | 0% | 2,918 | 7,086 | +143% | 0 | 0 | — |
case-07 | fail→pass | 26,995 | 8,556 | -68% | 1 | 1 | 0% | 4,544 | 7,264 | +60% | 0 | 0 | — |
case-08 | pass→pass | 15,141 | 10,697 | -29% | 1 | 1 | 0% | 1,584 | 7,541 | +376% | 0 | 0 | — |
case-09 | pass→pass | 13,948 | 9,614 | -31% | 1 | 1 | 0% | 1,619 | 7,458 | +361% | 0 | 0 | — |
case-10 | fail→pass | 19,989 | 15,286 | -24% | 1 | 1 | 0% | 3,006 | 8,768 | +192% | 0 | 0 | — |
case-11 | pass→pass | 20,951 | 13,839 | -34% | 1 | 1 | 0% | 2,951 | 8,237 | +179% | 0 | 0 | — |
case-12 | fail→pass | 20,238 | 16,373 | -19% | 1 | 1 | 0% | 2,570 | 8,684 | +238% | 0 | 0 | — |
case-13 | fail→pass | 13,403 | 10,426 | -22% | 1 | 1 | 0% | 1,400 | 7,671 | +448% | 0 | 0 | — |
case-14 | pass→pass | 11,239 | 8,438 | -25% | 1 | 1 | 0% | 1,062 | 7,339 | +591% | 0 | 0 | — |
case-15 | fail→pass | 19,272 | 8,641 | -55% | 1 | 1 | 0% | 2,492 | 7,323 | +194% | 0 | 0 | — |
case-16 | fail→pass | 24,406 | 10,431 | -57% | 1 | 1 | 0% | 2,970 | 7,629 | +157% | 0 | 0 | — |
case-17 | fail→pass | 16,936 | 12,909 | -24% | 1 | 1 | 0% | 2,087 | 8,069 | +287% | 0 | 0 | — |
case-18 | pass→pass | 20,561 | 15,133 | -26% | 1 | 1 | 0% | 2,856 | 8,600 | +201% | 0 | 0 | — |
case-19 | pass→pass | 20,646 | 12,067 | -42% | 1 | 1 | 0% | 2,669 | 7,982 | +199% | 0 | 0 | — |
case-20 | fail→pass | 25,180 | 15,681 | -38% | 1 | 1 | 0% | 3,085 | 8,644 | +180% | 0 | 0 | — |
case-21 | fail→pass | 18,437 | 11,128 | -40% | 1 | 1 | 0% | 2,314 | 7,830 | +238% | 0 | 0 | — |
case-22 | pass→pass | 19,230 | 13,983 | -27% | 1 | 1 | 0% | 2,329 | 8,233 | +253% | 0 | 0 | — |
case-23 | fail→fail | 14,308 | 15,309 | +7% | 1 | 1 | 0% | 1,587 | 8,633 | +444% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +48 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.