diff --git a/.mcp.json b/.mcp.json deleted file mode 100644 index 695bb8f..0000000 --- a/.mcp.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "mcpServers": { - "linear": { - "command": "npx", - "args": ["-y", "mcp-remote", "https://mcp.linear.app/sse"] - } - } -} \ No newline at end of file diff --git a/.yarnrc.yml b/.yarnrc.yml index a1feb90..3186f3f 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,3 +1 @@ nodeLinker: node-modules - -yarnPath: .yarn/releases/yarn-1.22.22.cjs diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 1c9623a..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,285 +0,0 @@ -# Squads Smart Account Program Summary - -## Core Architecture - -**Smart Account** = Multi-signature wallet with configurable governance and spending controls - -## Three Transaction Execution Modes - -### 1. Consensus-Based Transactions (Full Governance) -- **Purpose**: Any blockchain operation requiring multi-sig approval -- **Flow**: Create Transaction → Create Proposal → Vote → Time Lock → Execute -- **Components**: - - **Settings**: Signers, threshold, time lock configuration - - **Proposals**: Voting records (approved/rejected/cancelled) - - **Permissions**: Initiate, Vote, Execute roles - - **Stale Protection**: Settings changes invalidate old proposals - -### 2. Synchronous Transactions (Immediate Execution) -- **Purpose**: Same as consensus transactions but executed immediately -- **Flow**: Single instruction with all required signatures -- **Requirements**: - - Time lock must be 0 (no deliberation period) - - All required signers present simultaneously - - Combined permissions must include Initiate + Vote + Execute - - Meet threshold requirements -- **Benefits**: Gas efficient, atomic execution, reduced latency -- **Limitations**: No time lock support, requires coordination of all signers - -### 3. Spending Limits (Pre-Authorized Bypass) -- **Purpose**: Token transfers within pre-approved parameters -- **Flow**: Create Spending Limit → Use Spending Limit (direct execution) -- **Parameters**: - - Amount & token type - - Reset periods (OneTime, Daily, Weekly, Monthly) - - Authorized signers - - Destination allowlist - - Expiration date - -## Key Security Mechanisms - -### Stale Transaction Protection -- `transaction_index`: Latest transaction number -- `stale_transaction_index`: Staleness boundary -- When signers/threshold/timelock change → all pending transactions become stale -- Settings transactions cannot execute if stale (security critical) -- Regular transactions can execute if stale (but approved before staleness) - -### Permission System -- **Initiate**: Create transactions/proposals -- **Vote**: Approve/reject/cancel proposals -- **Execute**: Execute approved transactions - -### Time Locks -- Mandatory delay between approval and execution -- Prevents immediate execution attacks -- Can be removed by settings authority - -## Account Types -- **Autonomous**: Self-governed via proposals -- **Controlled**: Has settings authority that can bypass consensus for configuration - -## Transaction Mode Comparison -- **Consensus Transactions**: Any operation, full governance with voting + time lock -- **Synchronous Transactions**: Any operation, immediate execution with all signatures -- **Spending Limits**: Token transfers only, pre-authorized bypass -- All three are separate flows with no overlap or conflict - -This creates a flexible system balancing security (consensus) with efficiency (spending limits) for treasury management. - -## Policy Framework (Proposed Extension) - -### Overview -Spending limits are being evolved into a generalized **Policy Framework** where spending limits become just one type of policy among many. This creates a unified governance system for all types of smart account policies. - -### Policy Account Structure (Conceptual) -```rust -pub struct Policy { - // Core governance (unified across all policy types) - pub smart_account: Pubkey, // Parent smart account - pub transaction_index: u64, // Stale transaction protection - pub stale_transaction_index: u64, // Staleness boundary - pub signers: Vec, // Members with permissions - pub threshold: u16, // Approval threshold - pub time_lock: u32, // Delay before execution - - // Policy-specific configuration - pub policy_type: PolicyType, // Enum defining policy type - pub policy_data: Vec, // Serialized policy-specific parameters - pub vault_scopes: Vec, // Which vaults this policy applies to - - // Metadata - pub bump: u8, - pub created_at: i64, - pub updated_at: i64, -} - -pub struct PolicySigner { - pub key: Pubkey, - pub permissions: Permissions, // Initiate, Vote, Execute -} - -pub enum PolicyType { - SpendingLimit, // Current spending limits (token transfers) - ProgramInteraction, // Arbitrary program calls with constraints - // Future types: MultiSigOverride, ComplianceRule, etc. -} -``` - -### Policy Requirements (From GRID-600) -1. **Every signer on every policy has permissions** - PolicySigner structure with Initiate/Vote/Execute -2. **Every policy has thresholds** - Unified threshold field across all policy types -3. **Every policy can specify which vaults it's active on** - vault_scopes field -4. **Every policy offers alternate form of consensus** - Alternative to full smart account governance -5. **Overflowing amounts per spending limit duration** - Enhanced spending limit features -6. **Start date for spending limits** - Time-based activation -7. **Declarable remaining amount in Spending Limits** - Better limit tracking - -### Policy Types - -#### SpendingLimit Policy (Current Implementation → Policy Type) -- **Purpose**: Token transfers within pre-approved parameters -- **Parameters**: Amount, token, period, destinations, expiration -- **Execution**: Direct bypass of consensus when conditions met - -#### ProgramInteraction Policy (Proposed) -- **Purpose**: Allow specific program calls with granular constraints -- **Constraint Types**: - - **Instruction Data**: Bytes 0-8 (discriminator) must equal specific values - - **Account Constraints**: Specific accounts or accounts owned by certain programs - - **Parameter Validation**: Validate specific instruction data fields - -**Example Policy Configurations**: -```rust -pub struct ProgramInteractionConstraints { - pub program_id: Pubkey, - pub allowed_discriminators: Vec<[u8; 8]>, // Instruction discriminators allowed - pub account_constraints: Vec, - pub data_constraints: Vec, // Validate specific data fields -} - -pub enum AccountConstraint { - MustBe(Pubkey), // Account must be specific pubkey - MustBeOwnedBy(Pubkey), // Account must be owned by program - MustBeDerivedFrom { program: Pubkey, seeds: Vec> }, // PDA constraints -} - -pub enum DataConstraint { - BytesAtOffset { offset: usize, bytes: Vec }, // Specific bytes at offset - U64AtOffset { offset: usize, max_value: u64 }, // Numeric constraints -} -``` - -**Use Cases**: -- "Allow Jupiter swaps but only for amounts < 1000 USDC" -- "Allow Serum DEX orders but only on specific markets" -- "Allow staking but only to approved validators" -- **Execution**: Validate instruction matches all policy constraints before execution - -### Consensus Trait/Interface (Core Design Concept) -Since policy accounts and smart account settings share so much structure (signers, thresholds, permissions, stale transaction protection), the plan is to create a **unified consensus trait/interface** that allows execution on both: - -```rust -pub trait Consensus { - fn signers(&self) -> &[ConsensusSigner]; - fn threshold(&self) -> u16; - fn time_lock(&self) -> u32; - fn transaction_index(&self) -> u64; - fn stale_transaction_index(&self) -> u64; - fn validate_execution(&self, signers: &[Pubkey]) -> Result<()>; - // etc. -} - -impl Consensus for Settings { /* smart account implementation */ } -impl Consensus for Policy { /* policy account implementation */ } -``` - -This means you can execute transactions/proposals on both smart account settings AND individual policy accounts using the same interface and validation logic. - -### Benefits -1. **Unified Governance**: All policies use same stale transaction protection, permissions, consensus -2. **Extensible**: New policy types can be added without changing core infrastructure -3. **Consistent UX**: All policies work the same way from governance perspective -4. **Vault Scoping**: Each policy can specify which smart account indices it applies to -5. **Alternative Consensus**: Policies provide lighter-weight governance than full proposals -6. **Code Reuse**: Same consensus logic works for both smart accounts and policies - -### Swig Wallet Deep Analysis (Anagram) -**Repository**: https://github.com/anagrambuild/swig-wallet - -After analyzing the source code, Swig implements a **sophisticated structured authorization system**, not a general-purpose VM: - -#### Core Architecture -- **Role-Based Access Control**: Each wallet has multiple roles, each with specific authorities and actions -- **Action-Based Permissions**: Predefined action types (TokenLimit, SolLimit, ProgramScope, StakeLimit, etc.) -- **Multi-Authority Support**: ED25519, SECP256k1, SECP256r1 cryptographic authorities -- **Session Management**: Time-bounded temporary permissions with slot-based expiration - -#### Key Policy Components (from state-x/) - -**Role Structure**: -```rust -// Each role contains: -- Authority type + data -- Set of actions/permissions -- Unique role ID -- Position/boundary markers -``` - -**Action Types**: -- **TokenLimit**: Per-mint spending limits with `current_amount` tracking -- **SolLimit**: Native SOL spending limits -- **ProgramScope**: External program interaction policies with 3 scope types: - - Basic (unrestricted) - - Fixed Limit (total amount cap) - - Recurring Limit (time-windowed amounts) -- **StakeLimit**: Staking operation constraints -- **All**: Unrestricted access - -#### Policy Enforcement Mechanism -1. **Pre-execution validation**: Check role permissions and limits -2. **Real-time tracking**: Update `current_amount` during execution -3. **Account snapshots**: Validate post-execution state changes -4. **Automatic resets**: Time-based limit resets for recurring policies - -#### Key Insights for Our Framework -1. **Structured vs VM**: They use predefined action enums, not arbitrary logic -2. **Real-time enforcement**: Validation happens during execution, not pre-authorization -3. **Granular tracking**: Per-mint, per-program, per-operation limits -4. **Session model**: Temporary permissions complement persistent roles -5. **Account snapshots**: Post-execution validation ensures policy compliance - -#### Arbitrary Instruction Execution Analysis - -**Program Scope System** (from `state-x/src/action/program_scope.rs`): -- **3 Scope Types**: - - **Basic**: Unrestricted program interaction - - **Fixed Limit**: Total amount cap across all interactions - - **Recurring Limit**: Time-windowed (slot-based) amount limits with automatic resets -- **Balance Tracking**: Reads specific byte ranges in account data to track balances/amounts -- **Automatic Resets**: Time-based limit resets using Solana slot numbers - -**Program Permission System** (from `state-x/src/action/program.rs`): -- **Explicit Program Allowlist**: 32-byte program ID matching for allowed interactions -- **Repeatable Permissions**: Multiple program interactions allowed per role -- **Strict Validation**: Exact byte-level matching of program identifiers - -**Instruction Execution Constraints** (from `program/src/actions/sign_v1.rs`): -- **Pre-Execution**: Role authentication, program scope check, account classification, balance validation -- **Execution Control**: Account snapshots, controlled execution, post-execution validation -- **Balance Tracking**: Updates `current_amount` for limit enforcement - -**What Swig Does NOT Constrain**: -- **No Instruction Discriminator Filtering**: Doesn't restrict specific instruction types within allowed programs -- **No Instruction Data Validation**: Doesn't parse or constrain instruction data content -- **No Account Constraint Logic**: Doesn't enforce "account must be owned by X" style rules - -**Swig's Execution Model**: -```rust -// Simple program allowlist + balance limits -ProgramScope { - program_id: Pubkey, // Which program can be called - scope_type: Basic/Fixed/Recurring, // How much interaction allowed - balance_range: (offset, length), // What balance field to track - current_amount: u64, // Real-time usage tracking -} -``` - -**Key Characteristics**: -- **Program-level permissions** rather than instruction-level granularity -- **"How much" focus** - tracks amounts/balances rather than constraining specific operations -- **Real-time enforcement** during execution with account snapshots -- **Balance tracking within account data** for DeFi interactions - -**Relevance**: Swig validates our policy account approach but with more sophisticated real-time enforcement and granular action tracking. Their "policy engine" is actually a structured permission system with predefined types. - -### Migration Path -- Current spending limits → SpendingLimit policy type -- Existing spending limit accounts can be migrated or left as-is -- New policies created using unified Policy account structure - -## Key File Locations -- Core state: `programs/squads_smart_account_program/src/state/` -- Instructions: `programs/squads_smart_account_program/src/instructions/` -- Tests: `tests/suites/examples/` -- SDK: `sdk/smart-account/` \ No newline at end of file diff --git a/EVENT_LOGGING_OVERVIEW.md b/EVENT_LOGGING_OVERVIEW.md deleted file mode 100644 index afd43b2..0000000 --- a/EVENT_LOGGING_OVERVIEW.md +++ /dev/null @@ -1,96 +0,0 @@ -# Event Logging System Overview - -The smart account program includes a custom event logging mechanism that allows program-controlled accounts to emit structured events. - -## Core Components - -### LogEvent Instruction - -**File**: `programs/squads_smart_account_program/src/instructions/log_event.rs` - -```rust -pub struct LogEventArgsV2 { - pub event: Vec, -} - -#[derive(Accounts)] -pub struct LogEvent<'info> { - #[account( - constraint = Self::validate_log_authority(&log_authority).is_ok(), - owner = crate::id(), - )] - pub log_authority: Signer<'info>, -} -``` - -The instruction itself is minimal - it accepts event data and validates the log authority but doesn't process the data. - -### Log Authority Validation - -Only accounts owned by the smart account program with non-empty, non-zero data can log events: - -```rust -pub fn validate_log_authority(log_authority: &Signer<'info>) -> Result<()> { - let data_len = log_authority.data_len(); - require!(data_len > 0, SmartAccountError::ProtectedInstruction); - - let uninit_data = vec![0; data_len]; - let data = log_authority.try_borrow_data()?; - require!( - &**data != &uninit_data, - SmartAccountError::ProtectedInstruction - ); - Ok(()) -} -``` - -This prevents unauthorized logging by ensuring only legitimate program accounts (with actual state) can emit events. - -## Event Types - -**File**: `programs/squads_smart_account_program/src/events/account_events.rs` - -The system defines structured events for major operations: - -- **CreateSmartAccountEvent**: New smart account creation -- **SynchronousTransactionEvent**: Sync transaction execution -- **SynchronousSettingsTransactionEvent**: Sync settings changes -- **AddSpendingLimitEvent**: Spending limit creation -- **RemoveSpendingLimitEvent**: Spending limit removal -- **UseSpendingLimitEvent**: Spending limit usage -- **AuthoritySettingsEvent**: Authority-based settings changes -- **AuthorityChangeEvent**: Authority transfers - -## Event Emission - -**File**: `programs/squads_smart_account_program/src/events/mod.rs` - -Events are emitted by constructing a cross-program invocation to the LogEvent instruction: - -```rust -impl SmartAccountEvent { - pub fn log<'info>(&self, authority_info: &LogAuthorityInfo<'info>) -> Result<()> { - let data = LogEventArgsV2 { - event: self.try_to_vec()?, - }; - - let ix = solana_program::instruction::Instruction { - program_id: authority_info.program.key(), - accounts: vec![AccountMeta::new_readonly( - authority_info.authority.key(), - true, - )], - data: instruction_data, - }; - - invoke_signed(&ix, &[authority_account_info], &[signer_seeds.as_slice()])?; - Ok(()) - } -} -``` -## File Locations - -- **Instruction**: `programs/squads_smart_account_program/src/instructions/log_event.rs` -- **Event Types**: `programs/squads_smart_account_program/src/events/account_events.rs` -- **Event Logic**: `programs/squads_smart_account_program/src/events/mod.rs` -- **Entry Point**: `programs/squads_smart_account_program/src/lib.rs:337-342` \ No newline at end of file diff --git a/POLICIES_OVERVIEW.md b/POLICIES_OVERVIEW.md deleted file mode 100644 index 154a050..0000000 --- a/POLICIES_OVERVIEW.md +++ /dev/null @@ -1,195 +0,0 @@ -# Policy System Overview - -The policy system provides an alternative consensus mechanism to full smart account governance. Policies allow pre-approved, parameterized actions that can be executed with reduced overhead compared to the full proposal/voting process. - -## Core Architecture - -### Consensus Interface - -Both smart account settings and policies implement the same `Consensus` trait, allowing them to be used interchangeably for transaction validation. The `ConsensusAccount` enum wraps both types and provides unified access to consensus operations. - -**File**: `programs/squads_smart_account_program/src/interface/consensus.rs` - -```rust -#[derive(Clone)] -pub enum ConsensusAccount { - Settings(Settings), - Policy(Policy), -} -``` - -### Policy Account Structure - -**File**: `programs/squads_smart_account_program/src/state/policies/policy_core/policy.rs` - -```rust -pub struct Policy { - pub settings: Pubkey, // Parent smart account - pub seed: u64, // Unique policy identifier - pub transaction_index: u64, // Latest transaction number - pub stale_transaction_index: u64, // Staleness boundary - pub signers: Vec, // Members with permissions - pub threshold: u16, // Approval threshold - pub time_lock: u32, // Execution delay (seconds) - pub policy_state: PolicyState, // Type-specific configuration - pub start: i64, // Activation timestamp - pub expiration: Option, // Expiration rules -} -``` - -### Stale Transaction Protection - -Policies use the same stale transaction protection as smart account settings. When signers, threshold, or timelock change, all pending transactions become stale and cannot be executed. - -### Permission System - -Each policy signer has granular permissions: -- **Initiate**: Create transactions/proposals -- **Vote**: Approve/reject/cancel proposals -- **Execute**: Execute approved transactions - -## Policy Lifecycle - -### Policy Creation - -Policies are created through `SettingsAction::PolicyCreate` in a settings transaction: - -**File**: `programs/squads_smart_account_program/src/state/settings_transaction.rs` - -```rust -SettingsAction::PolicyCreate { - policy_seed: u64, - policy_signers: Vec, - policy_threshold: u16, - policy_time_lock: u32, - policy_creation_payload: PolicyCreationPayload, - policy_start: i64, - policy_expiration: Option, -} -``` - -### Policy Updates - -Updates use `SettingsAction::PolicyUpdate` and require full smart account governance. Updates invalidate prior transactions via `invalidate_prior_transactions()`. - -### Policy Deletion - -Policies can be deleted using `SettingsAction::PolicyRemove`: - -**File**: `programs/squads_smart_account_program/src/state/settings_transaction.rs` - -```rust -SettingsAction::PolicyRemove { - policy: Pubkey // The policy account to remove -} -``` - -The deletion process verifies policy ownership and closes the account. - -## Policy Expiration - -**File**: `programs/squads_smart_account_program/src/state/policies/policy_core/policy.rs` - -```rust -pub enum PolicyExpiration { - /// Policy expires at a specific timestamp - Timestamp(i64), - /// Policy expires when the core settings hash mismatches the stored hash - SettingsState([u8; 32]), -} -``` - -- **Timestamp**: Unix timestamp-based expiration -- **SettingsState**: Hash-based expiration when parent smart account changes - -## Policy Types - -### 1. SpendingLimitPolicy - -**File**: `programs/squads_smart_account_program/src/state/policies/implementations/spending_limit_policy.rs` - -Allows token and SOL transfers within predefined parameters: -- Source account scoping (specific vault index) -- Destination allowlist -- Time-based limits with automatic resets -- Usage tracking - -### 2. SettingsChangePolicy - -**File**: `programs/squads_smart_account_program/src/state/policies/implementations/settings_change.rs` - -Pre-approved modifications to smart account settings: -- AddSigner with optional constraints -- RemoveSigner with optional constraints -- ChangeThreshold -- ChangeTimeLock - -### 3. ProgramInteractionPolicy - -**File**: `programs/squads_smart_account_program/src/state/policies/implementations/program_interaction.rs` - -Constrained execution of arbitrary program instructions: -- Program ID filtering -- Account constraints -- Data constraints (discriminators, amounts, etc.) -- Optional spending limits integration - -Data constraint system supports: -```rust -pub enum DataValue { - U8(u8), U16Le(u16), U32Le(u32), U64Le(u64), U128Le(u128), - U8Slice(Vec), // For discriminators -} - -pub enum DataOperator { - Equals, NotEquals, GreaterThan, GreaterThanOrEqualTo, - LessThan, LessThanOrEqualTo, -} -``` - -### 4. InternalFundTransferPolicy - -**File**: `programs/squads_smart_account_program/src/state/policies/implementations/internal_fund_transfer.rs` - -Transfer funds between smart account vaults: -- Bitmask representation of allowed source/destination indices -- Mint allowlist (optional) - -## Transaction Execution Modes - -### Consensus-Based (Asynchronous) -Full governance process: Policy Transaction → Proposal → Voting → Time Lock → Execution - -### Synchronous Execution -Direct execution with all required signatures present. Requires time lock = 0 and all signers present. - -### Policy-Specific Execution -Direct policy execution bypassing consensus for pre-approved actions like spending limits. - -## Transaction Cleanup - -### Close Empty Policy Transaction - -**File**: `programs/squads_smart_account_program/src/instructions/transaction_close.rs` - -Cleans up transactions and proposals associated with deleted policy accounts. Validates that the policy account is empty and owned by the system program, then allows unconditional cleanup since the policy address can never be reused. - -## File Locations - -**Core Policy System**: -- `programs/squads_smart_account_program/src/state/policies/policy_core/` -- `programs/squads_smart_account_program/src/interface/consensus.rs` - -**Policy Implementations**: -- `programs/squads_smart_account_program/src/state/policies/implementations/` - -**Settings Integration**: -- `programs/squads_smart_account_program/src/state/settings.rs` -- `programs/squads_smart_account_program/src/state/settings_transaction.rs` - -**Instructions**: -- `programs/squads_smart_account_program/src/instructions/transaction_close.rs` -- `programs/squads_smart_account_program/src/lib.rs` (entry points) - -**Tests**: -- `tests/suites/instructions/policy*.ts` \ No newline at end of file diff --git a/README.md b/README.md index 2f84cf1..b8501dc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Squads Smart Account Program v0.1 +# Squads Smart Account Program Frame 13 @@ -8,122 +8,514 @@ [version-image]: https://img.shields.io/badge/version-0.1.0-blue.svg?style=flat [license-image]: https://img.shields.io/badge/license-AGPL_3.0-blue.svg?style=flat -We are developing the Smart Account Program to address programmability and cost limitations related to deploying smart contract wallets at scale on Solana. +High-performance smart wallet infrastructure for Solana. Built for stablecoin systems, programmable wallets, and financial applications at scale. -In order to deliver on this promise it builds on the following innovations: +## Table of Contents -- **Rent free wallet creation**: deploy wallets for as little as 0,0000025 SOL without paying rent until the account needs to execute transactions. Allowing developers to generate addresses for users at scale without worrying about deployment costs. -- **Atomic policy enforcement and transaction execution**: this program empowers developers with optimized smart contract wallets fit for building fully onchain PSPs, stablecoin banks and programmable wallets alike. -- **Archivable accounts**: recoup rent costs from inactive accounts without compromising on security using state compression. Enabling developers to confidently cover fees for their user's accounts by removing the burden of costs associated with inactive users. -- **Policies**: set rules on which an account can execute transactions and extend your account's functionality by creating your own policy programs. +- [Overview](#overview) +- [Grid: The Easiest Way to Build](#grid-the-easiest-way-to-build) +- [Quick Reference](#quick-reference) +- [Core Concepts](#core-concepts) + - [Settings Account](#settings-account) + - [Sub-Accounts (Vaults)](#sub-accounts-vaults) + - [Signers & Permissions](#signers--permissions) + - [Time Lock](#time-lock) + - [Stale Transaction Protection](#stale-transaction-protection) + - [Account Types](#account-types) +- [Execution Modes](#execution-modes) + - [Consensus Transactions (Async)](#consensus-transactions-async) + - [Synchronous Transactions](#synchronous-transactions) +- [Policy Framework](#policy-framework) + - [Program Interaction Policy (Smart Transactions)](#program-interaction-policy-smart-transactions) + - [Spending Limit Policy](#spending-limit-policy) + - [Internal Fund Transfer Policy](#internal-fund-transfer-policy) + - [Settings Change Policy](#settings-change-policy) +- [Legacy Spending Limits](#legacy-spending-limits) +- [Developers](#developers) +- [Building](#building) +- [Testing](#testing) +- [Verifying the Program](#verifying-the-program) +- [Responsibility](#responsibility) +- [Security](#security) +- [License](#license) -The Smart Account Program is in active development, with regular updates posted on [squads.so/blog](http://squads.so/blog) and [@SquadsProtocol](https://x.com/SquadsProtocol) on X. +## Overview -## Content +Smart contract wallets have traditionally forced developers to choose between programmability, cost, and security. The Smart Account Program eliminates this trilemma: -This repository contains: +- **Programmability**: Multi-signature governance, granular permissions, and flexible policies (spending limits, time locks, program interaction rules) +- **Cost**: Atomic policy enforcement and transaction execution in a single operation. Rent-free account creation allows deploying wallets for as little as 0.0000025 SOL, with rent deferred until the account executes transactions +- **Security**: Audited by OtterSec and formally verified by Certora. Supports native keypairs, MPC/TEE signing, with passkeys and alternative signature schemes coming soon -- The Squads Smart Account v0.1 program. -- The `@sqds/smart-account` Typescript SDK to interact with the smart account program. +For the full announcement, see [Smart Account Program: Live on Mainnet](https://squads.xyz/blog/squads-smart-account-program-live-on-mainnet). -## Program (Smart contract) Addresses +## Grid: The Easiest Way to Build -The Squads Smart Account Program v0.1 is deployed to: +**Don't want to integrate directly with the program?** [Grid](https://squads.xyz/grid) is our developer platform that abstracts the Smart Account Program into a simple API. -- Solana Mainnet-beta: `SMRTzfY6DfH5ik3TKiyLFfXexV8uSG3d2UksSCYdunG` -- Solana Devnet: `SMRTzfY6DfH5ik3TKiyLFfXexV8uSG3d2UksSCYdunG` +With Grid you get: +- **Stablecoin accounts** - policy-ready, fault-tolerant, proven in production +- **Payments** - stablecoins + ACH, Wire, SEPA rails +- **Card infrastructure** - programmable spend controls +- **Yield integrations** - RWA and DeFi strategies +- **Gas abstraction** - seamless UX without native tokens +- **Reconciliation** - unified ledger across all rails -Both deployments can be verified using the [Ellipsis Labs verifiable build](https://github.com/Ellipsis-Labs/solana-verifiable-build) tool. +Grid is trusted by 400+ teams securing $15B+. If you're building fintech, neobanks, or consumer apps, start with [Grid's documentation](https://grid.squads.xyz/welcome). -## Responsibility +For the full vision, see [Grid: A Stablecoin API for Accounts, Payments, Cards and Yield](https://squads.xyz/blog/grid-a-stablecoin-api-for-accounts-payments-cards-and-yield). -By interacting with this program, users acknowledge and accept full personal responsibility for any consequences, regardless of their nature. This includes both potential risks inherent to the smart contract, also referred to as program, as well as any losses resulting from user errors or misjudgment. +--- -By using a smart account, it is important to acknowledge certain concepts. Here are some that could be misunderstood by users: +*The rest of this README documents the underlying Smart Account Program for developers who need direct protocol access.* -- Loss of Private Keys: If a participant loses their private key, the smart account may not be able to execute transactions if a threshold number of signatures is required. -- Single Point of Failure with Keys: If all keys are stored in the same location or device, a single breach can compromise the smart account. -- Forgetting the Threshold: Misremembering the number of signatures required can result in a deadlock, where funds cannot be accessed. -- No Succession Planning: If keyholders become unavailable (e.g., due to accident, death), without a plan for transition, funds may be locked forever. -- Transfer of funds to wrong address: Funds should always be sent to the smart account account, and not the smart account settingsaddress. Due to the design of the Squads Protocol program, funds deposited to the smart account may not be recoverable. -- If the settings_authority of a smart account is compromised, an attacker can change smart account settings, potentially reducing the required threshold for transaction execution or instantly being able to remove and add new members. -- If the underlying SVM compatible blockchain undergoes a fork and a user had sent funds to the orphaned chain, the state of the blockchain may not interpret the owner of funds to be original one. -- Users might inadvertently set long or permanent time-locks in their smart account, preventing access to their funds for that period of time. -- Smart account participants might not have enough of the native token of the underlying SVM blockchain to pay for transaction and state fees. +## Quick Reference -## Developers +| Network | Address | +|---------|---------| +| Mainnet | `SMRTzfY6DfH5ik3TKiyLFfXexV8uSG3d2UksSCYdunG` | +| Devnet | `SMRTzfY6DfH5ik3TKiyLFfXexV8uSG3d2UksSCYdunG` | -You can interact with the Squads Smart Account Program via our SDKs. +## Core Concepts -List of SDKs: +### Settings Account -- Typescript SDK: `@sqds/smart-account` (not yet published) +The Settings account is the central configuration for a Smart Account. -## Compiling and testing +**PDA Derivation:** +``` +seeds = ["smart_account", "settings", seed] +``` -You can compile the code with Anchor. +**Account Structure:** +```rust +Settings { + seed: u128, // Unique identifier assigned by program config + settings_authority: Pubkey, // Controls settings (default = autonomous) + threshold: u16, // Required votes for approval + time_lock: u32, // Seconds between approval and execution + transaction_index: u64, // Latest transaction number + stale_transaction_index: u64, // Staleness boundary + archival_authority: Option, // Reserved for compression feature + archivable_after: u64, // Timestamp for archival eligibility + bump: u8, // PDA bump + signers: Vec, // Signers with permissions + account_utilization: u8, // Number of sub-accounts in use + policy_seed: Option, // Counter for deterministic policy creation +} + +SmartAccountSigner { + key: Pubkey, + permissions: Permissions { mask: u8 }, +} +``` +### Sub-Accounts (Vaults) + +Assets are held in sub-accounts derived from the Settings account. Each sub-account is identified by an `account_index` (u8), allowing up to 256 vaults per Smart Account. + +**PDA Derivation:** ``` -anchor build +seeds = ["smart_account", settings_key, "smart_account", account_index] ``` -If you do not have the Solana Anchor framework CLI installed, you can do so by following [this guide](https://www.anchor-lang.com/docs/installation). +Sub-account 0 is typically the primary vault. + +### Signers & Permissions + +Each signer has a pubkey and a permission bitmask controlling what actions they can perform: + +| Permission | Bit | Description | +|------------|-----|-------------| +| Initiate | `0b001` | Create transactions and proposals | +| Vote | `0b010` | Approve or reject proposals | +| Execute | `0b100` | Execute approved transactions | + +Permissions combine freely. Examples: +- `0b111` (7) - Full access: can initiate, vote, and execute +- `0b011` (3) - Can initiate and vote, but not execute +- `0b110` (6) - Can vote and execute, but not initiate + +**Threshold** defines how many Vote permissions are required for approval. A 2-of-3 multisig means threshold=2 with at least 3 signers having Vote permission. + +**Invariants enforced by the program:** +- At least one signer must have Initiate permission +- At least one signer must have Vote permission +- At least one signer must have Execute permission +- Threshold must be > 0 and <= number of voters + +### Time Lock + +Configurable delay (in seconds) between proposal approval and execution. Range: 0 to 90 days. + +- Time lock of 0 enables synchronous transactions (immediate execution) +- Non-zero time lock requires waiting period after approval + +### Stale Transaction Protection + +When signers, threshold, or time lock change, the program updates `stale_transaction_index` to the current `transaction_index`. This invalidates all pending settings transactions, preventing outdated proposals from executing under new governance rules. + +### Account Types + +**Autonomous:** `settings_authority = Pubkey::default()` +- All configuration changes go through proposal voting +- Fully self-governed -To deploy the program on a local validator instance for testing or development purposes, you can create a local instance by running this command from the [Solana CLI](https://docs.solana.com/cli/install-solana-cli-tools). +**Controlled:** `settings_authority = ` +- The settings authority can modify configuration directly +- Useful for managed/custodial setups +## Execution Modes + +### Consensus Transactions (Async) + +Full governance flow with voting and time lock. Use when signers aren't available simultaneously or when you need a deliberation period. + +**Flow:** ``` -solana-test-validator +Create Transaction → Create Proposal → Vote (until threshold) → [Time Lock] → Execute ``` -To run the tests, first install the node modules for the repository. +**Accounts involved:** + +```rust +Transaction { + settings: Pubkey, // Parent settings account + creator: Pubkey, // Who created it + index: u64, // Transaction index + bump: u8, + account_index: u8, // Which vault executes this + ephemeral_signer_bumps: Vec, + message: SmartAccountMessage, // The actual instruction(s) +} + +Proposal { + settings: Pubkey, + transaction_index: u64, + status: ProposalStatus, // Active, Approved, Rejected, Cancelled, Executed + bump: u8, + approved: Vec, // Signers who approved + rejected: Vec, // Signers who rejected + cancelled: Vec, // Signers who cancelled + activation_timestamp: i64, // When time lock ends +} +``` + +**Lifecycle:** +1. **Create Transaction**: Store the instruction(s) to execute +2. **Create Proposal**: Initialize voting state (status = Active) +3. **Vote**: Signers approve/reject. Once threshold reached, status = Approved and `activation_timestamp` is set +4. **Time Lock**: Wait until `current_time >= activation_timestamp` +5. **Execute**: Run the transaction. Status = Executed + +**Settings Transactions** follow the same flow but modify the Settings account itself (add/remove signers, change threshold, etc.). They have additional staleness checks: if the settings changed after proposal creation, execution fails. + +### Synchronous Transactions + +Atomic execution when all required signers are present. No intermediate accounts, no waiting. +**Requirements:** +- `time_lock = 0` on the Settings account +- All signing keys present in the transaction +- Combined permissions satisfy Initiate + Vote + Execute +- Number of voters meets threshold + +**Flow:** ``` -yarn +Single transaction with all signatures → Validate → Execute ``` -or +This is the most gas-efficient path when coordination is possible. Ideal for automated systems, MPC wallets, or situations where all parties are online. + +## Policy Framework + +Policies are scoped permission sets with their own governance. Each policy has independent signers, threshold, and time lock from the main Settings account. +**PDA Derivation:** ``` -npm install +seeds = ["smart_account", "policy", settings_key, policy_seed] ``` -And run these tests with this command: +**Account Structure:** +```rust +Policy { + settings: Pubkey, // Parent settings account + seed: u64, // Unique seed (auto-incremented) + bump: u8, + transaction_index: u64, // For stale protection + stale_transaction_index: u64, + signers: Vec, // Policy-specific signers + threshold: u16, + time_lock: u32, + policy_state: PolicyState, // Type-specific configuration + start: i64, // Activation timestamp + expiration: Option, + rent_collector: Pubkey, // Receives rent on close +} +``` + +**Policy Types:** + +| Type | Purpose | +|------|---------| +| `SpendingLimit` | Token transfers with amount/time constraints | +| `InternalFundTransfer` | Move funds between sub-accounts | +| `ProgramInteraction` | Constrained external program calls (Smart Transactions) | +| `SettingsChange` | Delegated configuration changes | + +**Expiration Modes:** +- `Timestamp(i64)` - Expires at Unix timestamp +- `SettingsState([u8; 32])` - Expires when settings hash changes (signers/threshold/time_lock modified) +Policies are created via `SettingsAction::PolicyCreate`, `PolicyUpdate`, and `PolicyRemove`. + +### Program Interaction Policy (Smart Transactions) + +The Program Interaction Policy enables autonomous, rules-based transaction execution. Instead of deploying custom programs, you can define constraints that are enforced atomically onchain. + +**Why onchain rule enforcement matters:** Off-chain validation introduces a trust layer between intent and execution. If checks fail or are bypassed, transactions still execute. Onchain enforcement eliminates this since invalid rules make the transaction itself invalid. + +**Constraint Types:** + +```rust +ProgramInteractionPolicy { + account_index: u8, // Which vault executes + instructions_constraints: Vec, + pre_hook: Option, // Called before execution + post_hook: Option, // Called after execution + spending_limits: Vec, // Balance tracking +} + +InstructionConstraint { + program_id: Pubkey, // Allowed program + account_constraints: Vec, // Required accounts + data_constraints: Vec, // Instruction data rules +} ``` -yarn test + +**What you can constrain:** +- **Allowed programs** - Whitelist by program ID +- **Allowed instructions** - Filter by discriminator +- **Required accounts** - Mandate specific accounts in the transaction +- **Account data validation** - Check balances, flags, pubkeys, numeric thresholds +- **Spending limits** - Track and cap balance changes per period + +**Data Operators:** +```rust +DataOperator::Equals | NotEquals | GreaterThan | GreaterThanOrEqualTo | LessThan | LessThanOrEqualTo ``` -### Verifying the code +**Use cases:** DCA strategies, yield routing, conditional escrow, agentic trading, automated treasury management. + +For the full vision, see [Smart Transactions: A Primitive for Autonomous Finance](https://squads.xyz/blog/grid-smart-transactions-a-primitive-for-autonomous-finance-on-solana). -First, compile the programs code from the `Squads-Protocol/smart-account-program` Github repository to get its bytecode. +#### Hooks (Pre & Post Execution) +Hooks allow external programs to be invoked before and/or after the main transaction executes. This enables custom validation, logging, side effects, or integration with external protocols. + +```rust +Hook { + program_id: Pubkey, // Program to invoke + num_extra_accounts: u8, // Additional accounts beyond program ID + account_constraints: Vec, // Constraints on hook accounts + instruction_data: Vec, // Data passed to the hook + pass_inner_instructions: bool, // Forward inner tx instructions to hook +} ``` -git clone https://github.com/Squads-Protocol/smart-account-program.git + +**Execution flow:** +``` +[Pre-Hook] → Constraint Validation → Execute Transaction → [Post-Hook] ``` +**Key features:** +- **Pre-hooks** run before the main transaction - useful for validation, price checks, or state snapshots +- **Post-hooks** run after execution - useful for logging, notifications, or post-condition verification +- **`pass_inner_instructions`** - when true, the hook receives serialized inner instructions and their accounts, enabling the hook to inspect what's being executed +- Hooks are invoked via CPI with a dedicated **hook authority** PDA as signer + +**Use cases:** +- Oracle price validation before swaps +- Compliance checks before transfers +- Event emission for off-chain indexing +- Post-execution accounting or reconciliation + +### Spending Limit Policy + +Token transfers with periodic limits and optional accumulation. + +```rust +SpendingLimitPolicy { + source_account_index: u8, + destinations: Vec, // Empty = any destination + spending_limit: SpendingLimitV2 { + mint: Pubkey, // Token mint (default = SOL) + time_constraints: TimeConstraints { + start: i64, + period: Period, // Daily, Weekly, Monthly, etc. + expiration: Option, + accumulate_unused: bool, // Roll over unused amounts + }, + quantity_constraints: QuantityConstraints { + max_per_period: u64, + max_per_use: u64, + enforce_exact_quantity: bool, + }, + usage: UsageState { + remaining_in_period: u64, + last_reset: i64, + }, + }, +} ``` + +### Internal Fund Transfer Policy + +Move assets between sub-accounts within the same Smart Account. + +```rust +InternalFundTransferPolicy { + source_account_mask: [u8; 32], // Bitmask of allowed source indices + destination_account_mask: [u8; 32], // Bitmask of allowed destination indices + allowed_mints: Vec, // Empty = any mint +} +``` + +Uses bitmasks for efficient storage - supports all 256 possible sub-account indices. + +### Settings Change Policy + +Delegate specific configuration changes without full governance. + +```rust +SettingsChangePolicy { + allowed_actions: Vec, +} +``` + +Allows policies to modify Settings (add/remove signers, change threshold, etc.) within defined bounds. + +## Legacy Spending Limits + +Before the Policy Framework, spending limits were standalone accounts managed directly via Settings actions. These remain supported for backwards compatibility. + +**PDA Derivation:** +``` +seeds = ["smart_account", settings_key, "spending_limit", seed] +``` + +```rust +SpendingLimit { + settings: Pubkey, + seed: Pubkey, // User-provided seed for PDA + account_index: u8, // Which vault + mint: Pubkey, // Token mint (default = SOL) + amount: u64, // Max per period + period: Period, // OneTime, Day, Week, Month + remaining_amount: u64, // Current period remaining + last_reset: i64, // Last reset timestamp + bump: u8, + signers: Vec, // Any one can use the limit + destinations: Vec, // Allowed recipients (empty = any) + expiration: i64, // Expiration timestamp +} +``` + +Created via `SettingsAction::AddSpendingLimit`, removed via `SettingsAction::RemoveSpendingLimit`. + +**Key differences from Spending Limit Policy:** +- No independent governance (uses Settings signers) +- Simpler structure (no time constraints, no accumulation) +- User-provided seed vs auto-incremented + +## Developers + +You can interact with the Squads Smart Account Program via our SDKs. + +**SDKs:** +- TypeScript: [`@sqds/smart-account`](https://www.npmjs.com/package/@sqds/smart-account) + +## Building + +This program requires Solana 1.18.16. Install it via Agave: + +```bash +agave-install init 1.18.16 +``` + +Compile the program with Anchor: + +```bash anchor build ``` -Now, install the [Ellipsis Labs verifiable build](https://crates.io/crates/solana-verify) crate. +If you don't have Anchor CLI installed, follow [this guide](https://www.anchor-lang.com/docs/installation). + +After building the program, rebuild the SDK to generate updated types: +```bash +yarn build ``` -cargo install solana-verify + +## Testing + +To deploy the program on a local validator for testing: + +```bash +solana-test-validator ``` -Get the executable hash of the bytecode from the Squads program that was compiled. +Install dependencies and run tests: +```bash +yarn +yarn test ``` -solana-verify get-executable-hash target/deploy/squads_smart_account_program.so + +## Verifying the Program + +Clone and build the program: + +```bash +git clone https://github.com/Squads-Protocol/smart-account-program.git +anchor build +``` + +Install [solana-verify](https://crates.io/crates/solana-verify): + +```bash +cargo install solana-verify ``` -Get the hash from the bytecode of the on-chain Squads program you want to verify. +Get the hash of your locally compiled program: +```bash +solana-verify get-executable-hash target/deploy/squads_smart_account_program.so ``` -solana-verify get-program-hash -u SMRTe6bnZAgJmXt9aJin7XgAzDn1XMHGNy95QATyzpk + +Compare with the on-chain program: + +```bash +solana-verify get-program-hash -u SMRTzfY6DfH5ik3TKiyLFfXexV8uSG3d2UksSCYdunG ``` -If the hash outputs of those two commands match, the code in the repository matches the on-chain programs code. +If the hashes match, the repository code matches the deployed program. + +## Responsibility + +By interacting with this program, users acknowledge and accept full personal responsibility for any consequences, regardless of their nature. This includes both potential risks inherent to the smart contract, also referred to as program, as well as any losses resulting from user errors or misjudgment. + +By using a smart account, it is important to acknowledge certain concepts. Here are some that could be misunderstood by users: + +- Loss of Private Keys: If a participant loses their private key, the smart account may not be able to execute transactions if a threshold number of signatures is required. +- Single Point of Failure with Keys: If all keys are stored in the same location or device, a single breach can compromise the smart account. +- Forgetting the Threshold: Misremembering the number of signatures required can result in a deadlock, where funds cannot be accessed. +- No Succession Planning: If keyholders become unavailable (e.g., due to accident, death), without a plan for transition, funds may be locked forever. +- Transfer of funds to wrong address: Funds should always be sent to the smart account account, and not the smart account settingsaddress. Due to the design of the Squads Protocol program, funds deposited to the smart account may not be recoverable. +- If the settings_authority of a smart account is compromised, an attacker can change smart account settings, potentially reducing the required threshold for transaction execution or instantly being able to remove and add new members. +- If the underlying SVM compatible blockchain undergoes a fork and a user had sent funds to the orphaned chain, the state of the blockchain may not interpret the owner of funds to be original one. +- Users might inadvertently set long or permanent time-locks in their smart account, preventing access to their funds for that period of time. +- Smart account participants might not have enough of the native token of the underlying SVM blockchain to pay for transaction and state fees. ## Security diff --git a/context.md b/context.md deleted file mode 100644 index d0cfe47..0000000 --- a/context.md +++ /dev/null @@ -1,176 +0,0 @@ -# Policy Account Implementation Context - -## Project Overview - -We are working on the **Squads Smart Account Program**, a Solana-based multi-signature wallet with configurable governance and spending controls. The project implements a unified policy framework that allows different types of policies to be executed through a common consensus mechanism. - -## Key Architecture Components - -### Smart Account Transaction Modes - -1. **Consensus-Based Transactions**: Full governance with multi-sig approval, voting, time locks -2. **Synchronous Transactions**: Immediate execution with all required signatures -3. **Spending Limits**: Pre-authorized token transfers within defined parameters -4. **Policy Framework**: Extensible system for custom governance rules - -### Core Policy Framework - -The policy system provides: -- **Unified Interface**: All policies implement the same consensus mechanism (signers, thresholds, time locks) -- **Type-Safe Execution**: Each policy type has its own validation and execution logic -- **Extensible Design**: New policy types can be added without breaking existing code - -## Recent Implementation Work - -### Phase 1: Enhanced Policy Execution Pattern (COMPLETED ✅) - -**Problem**: The original policy system had inconsistent execution patterns with manual pattern matching and unused abstractions. - -**Solution**: Implemented a clean trait-based execution system: - -1. **Enhanced PolicyExecutor Trait**: - ```rust - pub trait PolicyExecutor { - type Payload; - fn validate_payload(&self, payload: &Self::Payload) -> Result<()>; - fn execute_payload(&mut self, payload: &Self::Payload, accounts: &[AccountInfo]) -> Result<()>; - } - ``` - -2. **Type-Safe Dispatch Method**: - ```rust - impl PolicyType { - pub fn execute(&mut self, payload: &PolicyPayload, accounts: &[AccountInfo]) -> Result<()> { - match (self, payload) { - (PolicyType::InternalFundTransfer(policy), PolicyPayload::InternalFundTransfer(payload)) => { - policy.validate_payload(payload)?; - policy.execute_payload(payload, accounts) - } - _ => err!(SmartAccountError::InvalidPolicyPayload), - } - } - } - ``` - -3. **Clean Transaction Execution**: Replaced manual pattern matching with single dispatch call: - ```rust - // Before: Manual pattern matching with TODOs - // After: Clean dispatch - policy.policy_type.execute(payload, ctx.remaining_accounts)?; - ``` - -**Results**: -- ✅ All tests passing (3 execution pattern tests) -- ✅ Type safety with compile-time guarantees -- ✅ Preserved unified interface (`InterfaceAccount`) -- ✅ 90% code reuse for consensus logic - -### Phase 2: Directory Reorganization (COMPLETED ✅) - -**Problem**: The policy directory structure was messy and convoluted with mixed concerns. - -**Solution**: Implemented clean separation of concerns: - -``` -src/state/policies/ -├── core/ # Core policy framework -│ ├── traits.rs # PolicyExecutor trait -│ ├── policy.rs # Policy struct + PolicyType enum -│ ├── payloads.rs # PolicyPayload enum -│ └── creation_payloads.rs # Policy creation payloads -├── implementations/ # Specific policy implementations -│ ├── internal_fund_transfer.rs # Internal fund transfer policy -│ ├── spending_limit.rs # Spending limit policy -│ ├── program_interaction.rs # Program interaction policy -│ └── settings_change.rs # Settings change policy -└── tests/ # All policy tests - └── execution_pattern.rs # Framework execution tests -``` - -**Benefits**: -- ✅ Clear separation of framework vs implementations -- ✅ Easy navigation and extensibility -- ✅ Logical grouping of related functionality -- ✅ Clean imports with re-exports - -### Phase 3: Policy Creation via SettingsAction (IN PROGRESS 🔄) - -**Current Goal**: Implement policy creation and removal through SettingsAction for both synchronous and consensus-based execution. - -**Implementation Progress**: - -1. **Added PolicyCreationPayload Framework** ✅: - ```rust - #[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq, Eq)] - pub enum PolicyCreationPayload { - InternalFundTransfer(InternalFundTransferPolicyCreationPayload), - } - ``` - -2. **Extended SettingsAction Enum** ✅: - ```rust - pub enum SettingsAction { - // ... existing actions - PolicyCreate { - seed: Pubkey, - policy_creation_payload: PolicyCreationPayload, - signers: Vec, - threshold: u16, - time_lock: u32, - vault_scopes: Vec, - }, - PolicyRemove { - policy: Pubkey - }, - } - ``` - -3. **Implemented Policy Creation/Removal Logic** ✅: - - Added policy creation logic in `Settings::modify_with_action()` - - Handles PDA derivation, account creation, and serialization - - Added policy removal logic with proper ownership validation - - Integrated with existing rent payer and system program patterns - -**Next Steps**: -- Test compilation and fix any issues -- Add comprehensive tests for policy creation/removal -- Test integration with both sync and async settings transactions - -## Key Files and Locations - -### Core Framework -- `src/state/policies/core/policy.rs` - Main Policy struct and PolicyType enum -- `src/state/policies/core/traits.rs` - PolicyExecutor trait definition -- `src/state/policies/core/payloads.rs` - Unified policy payloads -- `src/state/policies/core/creation_payloads.rs` - Policy creation payloads - -### Policy Implementations -- `src/state/policies/implementations/internal_fund_transfer.rs` - Internal fund transfers -- `src/state/policies/implementations/spending_limit.rs` - Token spending limits -- `src/state/policies/implementations/program_interaction.rs` - Constrained program calls - -### Execution Infrastructure -- `src/instructions/transaction_execute.rs` - Policy execution via consensus -- `src/instructions/settings_transaction_execute.rs` - Settings actions via consensus -- `src/instructions/settings_transaction_sync.rs` - Settings actions synchronously -- `src/state/settings.rs` - Settings::modify_with_action() method - -### Tests -- `src/state/policies/tests/execution_pattern.rs` - Core framework tests - -## Design Principles - -1. **Type Safety**: Compile-time guarantees for policy-specific operations -2. **Unified Interface**: Single consensus mechanism for all policy types -3. **Extensibility**: New policies require only trait implementation -4. **Performance**: Zero serialization overhead for state changes -5. **Code Reuse**: 90% shared consensus logic across all policy types - -## Testing Strategy - -- Unit tests for policy validation logic -- Integration tests for creation/removal via SettingsAction -- End-to-end tests with both sync and async execution paths -- All tests use `cargo test --features no-entrypoint` to avoid allocator issues - -This implementation creates a robust, type-safe, and extensible policy framework that maintains the excellent unified interface while providing production-grade ergonomics for smart account governance. \ No newline at end of file diff --git a/sdk/smart-account/idl/squads_smart_account_program.json b/sdk/smart-account/idl/squads_smart_account_program.json index a20ccdd..e213347 100644 --- a/sdk/smart-account/idl/squads_smart_account_program.json +++ b/sdk/smart-account/idl/squads_smart_account_program.json @@ -6364,7 +6364,7 @@ "metadata": { "address": "SMRTzfY6DfH5ik3TKiyLFfXexV8uSG3d2UksSCYdunG", "origin": "anchor", - "binaryVersion": "0.29.0", + "binaryVersion": "0.32.1", "libVersion": "=0.29.0" } } \ No newline at end of file diff --git a/sdk/smart-account/scripts/fix-smallvec.js b/sdk/smart-account/scripts/fix-smallvec.js index f3a42f1..7ef43b3 100644 --- a/sdk/smart-account/scripts/fix-smallvec.js +++ b/sdk/smart-account/scripts/fix-smallvec.js @@ -41,7 +41,8 @@ const SMALLVEC_U8_BEET_TYPES_FILE_SPECIFIC = { 'CompiledAccountConstraintType.ts', ], 'beetSolana.publicKey': [ - 'SmartAccountTransactionMessage.ts', + // Note: SmartAccountTransactionMessage uses Vec, NOT SmallVec + // Only ProgramInteractionPolicyCreationPayload uses SmallVec 'ProgramInteractionPolicyCreationPayload.ts', ], }; @@ -87,7 +88,8 @@ function processFile(filePath) { } if (fileName === 'CompiledHook.ts') { - content = content.replace(/\['instructionData', beet\.bytes\]/g, "['instructionData', smallArray(beet.u8, beet.u8)]"); + // instruction_data: SmallVec (note: u16 length prefix) + content = content.replace(/\['instructionData', beet\.bytes\]/g, "['instructionData', smallArray(beet.u16, beet.u8)]"); content = content.replace(/instructionData: Uint8Array/g, 'instructionData: number[]'); } diff --git a/sdk/smart-account/src/generated/types/CompiledHook.ts b/sdk/smart-account/src/generated/types/CompiledHook.ts index 5fef632..c44bfb2 100644 --- a/sdk/smart-account/src/generated/types/CompiledHook.ts +++ b/sdk/smart-account/src/generated/types/CompiledHook.ts @@ -27,7 +27,7 @@ export const compiledHookBeet = new beet.FixableBeetArgsStruct( [ ['numExtraAccounts', beet.u8], ['accountConstraints', smallArray(beet.u8, compiledAccountConstraintBeet)], - ['instructionData', smallArray(beet.u8, beet.u8)], + ['instructionData', smallArray(beet.u16, beet.u8)], ['programIdIndex', beet.u8], ['passInnerInstructions', beet.bool], ], diff --git a/sdk/smart-account/src/generated/types/SmartAccountTransactionMessage.ts b/sdk/smart-account/src/generated/types/SmartAccountTransactionMessage.ts index 9682ad7..086b495 100644 --- a/sdk/smart-account/src/generated/types/SmartAccountTransactionMessage.ts +++ b/sdk/smart-account/src/generated/types/SmartAccountTransactionMessage.ts @@ -7,7 +7,6 @@ import * as web3 from '@solana/web3.js' import * as beet from '@metaplex-foundation/beet' -import { smallArray } from '../../types' import * as beetSolana from '@metaplex-foundation/beet-solana' import { SmartAccountCompiledInstruction, @@ -36,7 +35,7 @@ export const smartAccountTransactionMessageBeet = ['numSigners', beet.u8], ['numWritableSigners', beet.u8], ['numWritableNonSigners', beet.u8], - ['accountKeys', smallArray(beet.u8, beetSolana.publicKey)], + ['accountKeys', beet.array(beetSolana.publicKey)], ['instructions', beet.array(smartAccountCompiledInstructionBeet)], [ 'addressTableLookups', diff --git a/summary.md b/summary.md deleted file mode 100644 index 1a2bfdc..0000000 --- a/summary.md +++ /dev/null @@ -1,204 +0,0 @@ -# Policy Account Architecture Design Problem - -## Context - -The Squads Smart Account Program is implementing a unified policy framework where different types of policies (spending limits, program interactions, internal fund transfers, etc.) can be executed through a common consensus mechanism. The goal is to make policy execution as idiomatic and extensible as possible. - -## Current Architecture - -### Existing Structure -The current implementation uses a single `Policy` account with an enum-based approach: - -```rust -#[account] -pub struct Policy { - // Common consensus fields (90% shared across all policy types) - pub settings: Pubkey, - pub transaction_index: u64, - pub stale_transaction_index: u64, - pub signers: Vec, - pub threshold: u16, - pub time_lock: u32, - pub vault_scopes: Vec, - - // Policy type identification - pub policy_type: PolicyType, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone)] -pub enum PolicyType { - InternalFundTransfer(InternalFundTransferPolicy), - SpendingLimit(SpendingLimitPolicy), - // Future policy types... -} -``` - -### Current Execution Flow -1. **Transaction Creation**: `CreateTransactionArgs` enum handles both `TransactionPayload` (for settings) and `PolicyPayload` (for policies) -2. **Transaction Execution**: Pattern matching on `transaction.payload` type in `transaction_execute.rs` -3. **Policy Dispatch**: Manual pattern matching on policy type with incomplete execution logic - -### ConsensusAccount Trait -All consensus accounts (Settings and Policies) implement a unified `Consensus` trait that provides: -- Signer management (`signers()`, `is_signer()`, `signer_has_permission()`) -- Transaction indexing (`transaction_index()`, `set_transaction_index()`) -- Threshold and timelock management -- Stale transaction protection - -Instructions use `InterfaceAccount<'info, ConsensusAccount>` to work with any consensus account type uniformly. - -## The Problem - -### Current Issues -1. **Inconsistent Execution Pattern**: Each policy type requires manual pattern matching in execution logic -2. **Unused Abstractions**: `PolicyExecution` trait exists but isn't properly utilized -3. **Ad-hoc Dispatch**: `PolicyPayload::execute()` manually dispatches to policy implementations -4. **Maintenance Burden**: Adding new policy types requires changes to multiple match statements -5. **State Management Complexity**: Policy-specific data is embedded in enums, making field access verbose - -### Core Dilemma -The fundamental tension is between: -- **Type Safety & Performance**: Direct field access (`policy.field = value`) with compile-time guarantees -- **Unified Interface**: Single account type that works with `InterfaceAccount<'info, ConsensusAccount>` -- **Code Reuse**: Avoiding duplication of the 90% shared consensus fields across policy types - -## Explored Solutions - -### Option 1: Composition with Serialized Data -```rust -#[account] -pub struct Policy { - // Common consensus fields - pub settings: Pubkey, - pub transaction_index: u64, - // ... other consensus fields - - pub policy_discriminator: u8, - pub policy_data: Vec, // Serialized policy-specific data -} -``` - -**Pros**: Zero duplication, unified interface -**Cons**: Serialization overhead for every state change, loss of type safety - -### Option 2: Macro-Generated Separate Types -```rust -macro_rules! policy_account { - ($name:ident { $(pub $field:ident: $type:ty,)* }) => { - #[account] - pub struct $name { - // Auto-generated consensus fields - pub settings: Pubkey, - pub transaction_index: u64, - // ... - - // Policy-specific fields - $(pub $field: $type,)* - } - }; -} -``` - -**Pros**: Zero runtime overhead, full type safety, direct field access -**Cons**: Breaks `InterfaceAccount<'info, ConsensusAccount>` - multiple distinct types can't be treated uniformly - -### Option 3: Composition with Embedded Struct -```rust -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct PolicyConsensus { - pub settings: Pubkey, - pub transaction_index: u64, - // ... consensus fields -} - -#[account] -pub struct InternalFundTransferPolicy { - pub consensus: PolicyConsensus, - pub source_account_indices: Vec, - // ... policy-specific fields -} -``` - -**Pros**: Direct field access, type safety, minimal duplication -**Cons**: Still breaks unified interface, requires trait delegation boilerplate - -### Option 4: Enum for Interface with Separate Types -```rust -pub enum ConsensusAccountData { - Settings(Settings), - InternalFundTransferPolicy(InternalFundTransferPolicy), - SpendingLimitPolicy(SpendingLimitPolicy), -} - -impl ConsensusAccount for ConsensusAccountData { - fn signers(&self) -> &[PolicySigner] { - match self { - ConsensusAccountData::Settings(s) => &s.signers, - ConsensusAccountData::InternalFundTransferPolicy(p) => &p.consensus.signers, - // ... pattern match for each type - } - } -} -``` - -**Pros**: Maintains unified interface, type-safe field access -**Cons**: Method delegation boilerplate for every consensus method on every policy type - -## Core Architectural Constraints - -### Anchor Framework Limitations -- `InterfaceAccount<'info, T>` requires a single trait object type -- Account types must be known at compile time for space allocation -- Serialization format is fixed per account type - -### Performance Requirements -- State changes should be direct field assignments, not serialize/deserialize cycles -- Consensus operations (voting, execution) happen frequently and must be efficient - -### Extensibility Goals -- Adding new policy types should require minimal changes to existing code -- Policy execution should be pluggable and uniform -- Each policy should own its specific logic and data - -## Key Questions for Resolution - -1. **Interface vs Type Safety**: Is it acceptable to break the unified `InterfaceAccount` interface to gain type safety and performance? - -2. **Code Generation**: Are macros an acceptable solution for eliminating boilerplate, or do they introduce too much complexity? - -3. **Runtime Dispatch**: Is the performance cost of serialization/deserialization acceptable for the benefits of a unified interface? - -4. **Trait Delegation**: Is repetitive trait implementation across policy types an acceptable trade-off for direct field access? - -5. **Alternative Architectures**: Are there Rust/Anchor patterns we haven't considered that could solve this trilemma? - -## Success Criteria - -The ideal solution should provide: -- **Ergonomic state management**: `policy.field = value` level simplicity -- **Type safety**: Compile-time guarantees for policy-specific fields -- **Unified interface**: Works with existing consensus trait and instruction patterns -- **Zero/minimal duplication**: Don't repeat the 90% shared consensus logic -- **Extensibility**: Adding new policies requires only implementing policy-specific logic -- **Performance**: No unnecessary serialization overhead for state changes - -## Policy Types Context - -### Current Policy Types -- **InternalFundTransfer**: Transfer funds between smart account vaults -- **SpendingLimit**: Token spending limits with time constraints and usage tracking -- **ProgramInteraction**: Constrained program calls with instruction/data validation - -### Policy Execution Pattern -All policies follow the pattern: -1. Receive a policy-specific payload (execution parameters) -2. Validate the payload against policy constraints -3. Execute the action using provided `remaining_accounts` -4. Update policy state (usage tracking, etc.) - -### Consensus Integration -Policies implement the same consensus mechanism as Settings: -- Multi-signature approval with configurable thresholds -- Time locks between approval and execution -- Stale transaction protection when policy parameters change -- Permission-based access control (Initiate, Vote, Execute) \ No newline at end of file diff --git a/tests/index.ts b/tests/index.ts index 406bcc7..6b66665 100644 --- a/tests/index.ts +++ b/tests/index.ts @@ -1,34 +1,34 @@ // The order of imports is the order the test suite will run in. import "./suites/program-config-init"; -// import "./suites/examples/batch-sol-transfer"; -// import "./suites/examples/create-mint"; -// import "./suites/examples/immediate-execution"; -// import "./suites/examples/spending-limits"; -// import "./suites/examples/transaction-buffer"; -// import "./suites/instructions/batchAccountsClose"; -// import "./suites/instructions/cancelRealloc"; -// import "./suites/instructions/settingsTransactionAccountsClose"; -// import "./suites/instructions/settingsTransactionExecute"; -// import "./suites/instructions/settingsTransactionSynchronous"; -// import "./suites/instructions/smartAccountCreate"; -// import "./suites/instructions/smartAccountSetArchivalAuthority"; -// import "./suites/instructions/transactionBufferClose"; -// import "./suites/instructions/transactionBufferCreate"; -// import "./suites/instructions/transactionBufferExtend"; -// import "./suites/instructions/batchTransactionAccountClose"; -// import "./suites/instructions/transactionAccountsClose"; -// import "./suites/instructions/transactionCreateFromBuffer"; -// import "./suites/instructions/transactionSynchronous"; -// import "./suites/instructions/logEvent"; -// import "./suites/instructions/policyCreation"; -// import "./suites/instructions/policyUpdate"; -// import "./suites/instructions/removePolicy"; -// import "./suites/instructions/policyExpiration"; -// import "./suites/instructions/settingsChangePolicy"; +import "./suites/examples/batch-sol-transfer"; +import "./suites/examples/create-mint"; +import "./suites/examples/immediate-execution"; +import "./suites/examples/spending-limits"; +import "./suites/examples/transaction-buffer"; +import "./suites/instructions/batchAccountsClose"; +import "./suites/instructions/cancelRealloc"; +import "./suites/instructions/settingsTransactionAccountsClose"; +import "./suites/instructions/settingsTransactionExecute"; +import "./suites/instructions/settingsTransactionSynchronous"; +import "./suites/instructions/smartAccountCreate"; +import "./suites/instructions/smartAccountSetArchivalAuthority"; +import "./suites/instructions/transactionBufferClose"; +import "./suites/instructions/transactionBufferCreate"; +import "./suites/instructions/transactionBufferExtend"; +import "./suites/instructions/batchTransactionAccountClose"; +import "./suites/instructions/transactionAccountsClose"; +import "./suites/instructions/transactionCreateFromBuffer"; +import "./suites/instructions/transactionSynchronous"; +import "./suites/instructions/logEvent"; +import "./suites/instructions/policyCreation"; +import "./suites/instructions/policyUpdate"; +import "./suites/instructions/removePolicy"; +import "./suites/instructions/policyExpiration"; +import "./suites/instructions/settingsChangePolicy"; import "./suites/instructions/programInteractionPolicy"; -// import "./suites/instructions/spendingLimitPolicy"; -// import "./suites/instructions/internalFundTransferPolicy"; -// import "./suites/smart-account-sdk"; -// // // Uncomment to enable the heapTest instruction testing -// // //import "./suites/instructions/heapTest"; -// // import "./suites/examples/custom-heap"; +import "./suites/instructions/spendingLimitPolicy"; +import "./suites/instructions/internalFundTransferPolicy"; +import "./suites/smart-account-sdk"; +// // Uncomment to enable the heapTest instruction testing +// //import "./suites/instructions/heapTest"; +// import "./suites/examples/custom-heap"; diff --git a/tests/suites/instructions/policyCreation.ts b/tests/suites/instructions/policyCreation.ts index 19a30ac..244e84a 100644 --- a/tests/suites/instructions/policyCreation.ts +++ b/tests/suites/instructions/policyCreation.ts @@ -151,7 +151,7 @@ describe("Flows / Policy Creation", () => { const policyCreationPayload: smartAccount.generated.PolicyCreationPayload = { - __kind: "ProgramInteraction", + __kind: "LegacyProgramInteraction", fields: [ { accountIndex: 0, // Apply to account index 0