mcpField
S
squads

squads

github.com/sendaifun開発130195 回インストール13 週間前に更新
リポジトリ

squads について

squads は、マルチシグ、スマートアカウント、Grid のオープンファイナンス基盤を扱う Solana 開発者向けガイドです。提案の承認、プログラム可能なウォレット、ステーブルコイン決済を取り上げ、チームの資産管理や決済システムの構築に役立ちます。

主な機能

  1. 01Squads V4 によるマルチシグ提案と金庫の管理
  2. 02タイムロック、支出上限、バッチトランザクションの設定
  3. 03スマートアカウントのセッションキーとポリシーの設定
  4. 04パスキー認証
  5. 05Grid によるステーブルコイン決済と定期送金の構築

活用シーン

  1. 01チーム資金の提案作成、投票、トランザクション実行
  2. 02メンバー権限の制限と高額取引へのタイムロック追加
  3. 03ウォレットの委任アクセスと支出ポリシーの設定
  4. 04決済プラットフォームでのステーブルコイン送金と定期決済の実装

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/sendaifun/skills --skill squads

インストール後、Claude Code で/squadsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

docs/
troubleshooting.md
examples/grid/
api-quickstart.ts
payments.ts
examples/multisig/
create-multisig.ts
proposals-voting.ts
spending-limits.ts
vault-transactions.ts
examples/smart-account/
account-creation.ts
session-keys.ts
resources/
grid-api-reference.md
multisig-api-reference.md
program-addresses.md
smart-account-api-reference.md
templates/
grid-client.ts
multisig-setup.ts
smart-account-setup.ts

詳細

Squads Protocol 開発ガイド

Squads Protocol は Solana を代表するスマートアカウント基盤であり、100 億ドルを超えるデジタル資産を保護しています。このガイドでは、3 つの主要製品である Squads V4 Multisig、Smart Account Program、Grid について説明します。

概要

Squads Protocol が提供する機能は次のとおりです。

  • Squads V4 Multisig - チーム向けのマルチシグウォレットです。提案、投票、タイムロック、支出上限、プログラムのアップグレード管理に対応します
  • Smart Account Program - セッションキー、パスキー、プログラム可能なポリシー、口座振替を備えたアカウント抽象化基盤です
  • Grid - ステーブルコインの決済網、ネオバンクプラットフォーム、企業向け決済システムのためのオープンな金融基盤です

プログラム ID

プログラムMainnetDevnet
Squads V4 MultisigSQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCfSQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf
Smart AccountSMRTzfY6DfH5ik3TKiyLFfXexV8uSG3d2UksSCYdunGSMRTzfY6DfH5ik3TKiyLFfXexV8uSG3d2UksSCYdunG
External Signature (Grid)ExtSgUPtP3JyKUysFw2S5fpL5fWfUPzGUQLd2bTwftXNExtSgUPtP3JyKUysFw2S5fpL5fWfUPzGUQLd2bTwftXN

Eclipse Mainnet:

プログラムアドレス
Squads V4 MultisigeSQDSMLf3qxwHVHeTr9amVAGmZbRLY2rFdSURandt6f

クイックスタート

インストール
# 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
基本設定
import * 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 Multisig

Squads V4 はマルチシグプロトコルの最新バージョンで、セキュリティの強化、タイムロック、支出上限、一括トランザクションを備えています。

基本概念

マルチシグアカウント:メンバー、しきい値、タイムロック設定などの構成を保持するメインアカウントです。

ボルト:資産を保管する、マルチシグによって制御される PDA です。各マルチシグは複数のボルトを持つことができ(インデックスは 0、1、2...)、それぞれに資産を保管できます。

提案:マルチシグメンバーの承認を必要とする、トランザクション実行のリクエストです。

トランザクション:提案の承認後に実行される実際の命令です。

権限システム

メンバーには異なる権限を割り当てることができます。

import { 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]);
権限説明
Initiate新しい提案を作成できます
Vote提案を承認または却下できます
Execute承認済みの提案を実行できます
マルチシグの作成
import * 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);
ボルト操作

ボルトはマルチシグの資産を保管する PDA です。

// 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");

重要:資金はマルチシグアカウントではなく、必ずボルト PDA に送金してください。

ボルトトランザクションの作成
import { 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());
提案の作成と管理
// 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());
提案への投票
// 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",
});
承認済みトランザクションの実行
// 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);
}
支出上限

支出上限を設定すると、メンバーはマルチシグ全体の承認を得なくても、一定額までのトランザクションを実行できます。

// 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,
});
一括トランザクション

複数のトランザクションをアトミックに実行します。

// 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
設定トランザクション

マルチシグの設定を変更します(提案の承認が必要です)。

// 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,
  }],
});
タイムロック

承認済みトランザクションを実行するまでに遅延を設けます。

// 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,
});
PDA 導出リファレンス
import * 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,
});

Smart Account Program

Smart Account Program は Solana 向けのアカウント抽象化機能を提供し、セッションキー、パスキー、ポリシーベースの実行に対応したプログラム可能なウォレットを実現します。

基本概念

スマートアカウント:秘密鍵だけでなく、ポリシーによって制御されるプログラム可能なウォレットです。

セッションキー:特定の操作に限定された権限を持つ一時的なキーです。

パスキー:生体認証またはハードウェアキーを使用する WebAuthn/FIDO2 認証です。

ポリシー:実行できるトランザクションを定めるルールです。

機能
  • レンタル料不要のデプロイ - スマートアカウントの作成にレンタル料は不要です
  • アトミックなポリシー適用 - すべてのポリシーチェックがアトミックに実行されます
  • アカウント圧縮 - 複数の署名者を効率的に保存できます
  • 拡張可能なポリシー - カスタムポリシープログラムを追加できます
  • 口座振替 - 定期支払いを自動化します
  • サブスクリプション - サブスクリプションの請求を管理します
REST API ベース URL
https://developer-api.squads.so/api/v1
スマートアカウントの作成
// 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: "[email protected]",
    // or
    signer: signerPublicKey,
  }),
});

const account = await response.json();
console.log("Smart account:", account.address);
セッションキーの管理

セッションキーを使用すると、特定の権限でアクセスを委任できます。

// 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
      },
    }),
  }
);
パスキー認証
// 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,
    }),
  }
);
ポリシーの設定
// 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

Grid は、ネオバンクプラットフォームやエンタープライズ決済システムを支える、Squads のステーブルコイン向けオープンな金融インフラです。

機能
  • ステーブルコイン向けインフラ - USDC/USDT 決済インフラ
  • プログラム可能な決済 - 資金の流れを自動化
  • バーチャルアカウント - 法定通貨の入出金機能
  • KYC 連携 - コンプライアンスツールを内蔵
  • セルフカストディ - ユーザーが資産の管理権を維持
API 認証
// 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: "[email protected]",
  }),
});

// 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: "[email protected]",
    otp: "123456",
  }),
});

const { accessToken } = await verifyResponse.json();
アカウント管理
// 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);
決済処理
// 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);
定期注文(定期決済)
// 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",
    }),
  }
);
支出上限
// 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",
    }),
  }
);

ベストプラクティス

セキュリティ
  1. 秘密鍵を決して共有しない - 機密データには環境変数を使用します
  2. プログラム ID を検証する - 公式プログラムとやり取りしていることを必ず確認します
  3. タイムロックを使用する - 高額の資金を管理する場合は、セキュリティを強化するためにタイムロックを追加します
  4. 権限を制限する - メンバーには必要な権限のみを付与します
  5. devnet でテストする - mainnet で実行する前に、必ず devnet でトランザクションをテストします
トランザクションの最適化
  1. アドレスルックアップテーブルを使用する - 複雑な操作でトランザクションサイズを削減します
  2. トランザクションをまとめる - 可能な場合は、関連する操作をまとめます
  3. 適切なコンピュートバジェットを設定する - コンピュート制限によるトランザクションの失敗を防ぎます
import { 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,
});
エラー処理
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;
  }
}
アカウント状態の取得
// 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);

よくあるパターン

プログラムアップグレード用マルチシグ

マルチシグでプログラムのアップグレード権限を保護します。

// Transfer upgrade authority to multisig vault
const transferAuthIx = createSetAuthorityInstruction(
  programDataAddress,
  currentAuthority,
  AuthorityType.UpgradeAuthority,
  vaultPda
);

// Execute via multisig proposal
// Now program upgrades require multisig approval
資金管理
// 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
バリデーターの操作
// Manage validator identity and vote account with multisig
// Transfer validator identity to vault
// Set up spending limits for operational costs

リソース

公式ドキュメント
GitHub リポジトリ
セキュリティ監査
  • OtterSec の監査
  • Neodyme の監査
  • Certora の監査
  • Trail of Bits の監査
プログラムビルドの検証
# 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

コメント