> For the complete documentation index, see [llms.txt](https://docs.realms.today/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.realms.today/developer-resources/custom-instruction.md).

# Custom Instruction

## Create a Custom Instruction

SPL Governance proposals can execute arbitrary on-chain instructions once a vote succeeds. This is the core mechanism that makes DAOs powerful: any instruction that can be called by a Solana program can be governed by a vote.

### How Proposal Transactions Work

A Proposal can contain multiple **ProposalTransactions**, each with multiple instructions. After a successful vote, anyone can trigger execution once the `hold_up_time` has elapsed. The instructions are signed by the Governance PDA (specifically the DAO Wallet), giving them authority over any assets the DAO controls.

#### Two Transaction Types

SPL Governance v3.1.2 supports two types of proposal transactions:

1. **Legacy Transactions** (`InsertTransaction`) - The original format using `InstructionData`
2. **Versioned Transactions** (`InsertVersionedTransaction`) - New in v3.1.2, supports Address Lookup Tables and larger transactions

### Creating a Legacy Proposal Transaction

#### InstructionData Format

Each instruction in a proposal transaction is encoded as:

```rust
pub struct InstructionData {
    /// Target program ID
    pub program_id: Pubkey,
    /// Account metadata for the instruction
    pub accounts: Vec<AccountMetaData>,
    /// Instruction data bytes
    pub data: Vec<u8>,
}

pub struct AccountMetaData {
    pub pubkey: Pubkey,
    pub is_signer: bool,
    pub is_writable: bool,
}
```

#### Step-by-Step: Adding an Instruction to a Proposal

**1. Create the proposal:**

```rust
let proposal_seed = Pubkey::new_unique();

let create_proposal_ix = create_proposal(
    &governance_program_id,
    &governance,
    &proposal_owner_record,
    &governance_authority,
    &payer,
    None, // voter_weight_record
    &realm,
    "Transfer 100 SOL to Developer".to_string(),
    "https://forum.example.com/proposal-42".to_string(),
    &governing_token_mint,
    VoteType::SingleChoice,
    vec!["Approve".to_string()],
    true, // use_deny_option
    &proposal_seed,
);
```

**2. Insert a transaction with your custom instruction:**

```rust
// Example: Transfer SOL from the DAO treasury
let transfer_ix = system_instruction::transfer(
    &dao_wallet,      // source: the DAO's native treasury
    &recipient,       // destination
    100_000_000_000,  // 100 SOL in lamports
);

let instruction_data = vec![InstructionData {
    program_id: system_program::id(),
    accounts: transfer_ix.accounts.iter().map(|a| AccountMetaData {
        pubkey: a.pubkey,
        is_signer: a.is_signer,
        is_writable: a.is_writable,
    }).collect(),
    data: transfer_ix.data,
}];

let insert_tx_ix = insert_transaction(
    &governance_program_id,
    &governance,
    &proposal,
    &token_owner_record,
    &governance_authority,
    &payer,
    0,              // option_index (first option)
    0,              // transaction_index
    0,              // hold_up_time in seconds (0 = execute immediately after vote)
    instruction_data,
);
```

**3. Sign off the proposal to begin voting:**

```rust
let sign_off_ix = sign_off_proposal(
    &governance_program_id,
    &realm,
    &governance,
    &proposal,
    &governance_authority,
    Some(&proposal_owner_record),
);
```

### Creating Versioned Transactions (v3.1.2)

For larger instructions or when you need Address Lookup Tables, use versioned transactions:

#### Direct Insert (Small Transactions)

```rust
let insert_vtx_ix = insert_versioned_transaction(
    &governance_program_id,
    &governance,
    &proposal,
    &token_owner_record,
    &governance_authority,
    &payer,
    0,  // option_index
    0,  // ephemeral_signers count
    0,  // transaction_index
    transaction_message_bytes, // serialized VersionedMessage
);
```

#### Buffered Insert (Large Transactions)

For transactions that exceed the Solana transaction size limit, use the buffer mechanism:

```rust
// 1. Create a buffer
let create_buffer_ix = create_transaction_buffer(
    &governance_program_id,
    &governance,
    &proposal,
    &token_owner_record,
    &governance_authority,
    &payer,
    0, // buffer_index
    final_buffer_hash, // SHA-256 of the complete message
    final_buffer_size,
    initial_chunk, // first chunk of bytes
);

// 2. Extend the buffer with remaining chunks
let extend_buffer_ix = extend_transaction_buffer(
    &governance_program_id,
    &governance,
    &proposal,
    &payer,
    0, // buffer_index
    next_chunk, // next chunk of bytes
);

// 3. Insert the versioned transaction from the completed buffer
let insert_from_buffer_ix = insert_versioned_transaction_from_buffer(
    &governance_program_id,
    &governance,
    &proposal,
    &token_owner_record,
    &governance_authority,
    &payer,
    0, // option_index
    0, // ephemeral_signers
    0, // transaction_index
    0, // buffer_index
);
```

### Common Custom Instruction Patterns

#### Program Upgrade

The most common governance action - upgrading a program:

```rust
let upgrade_ix = bpf_loader_upgradeable::upgrade(
    &program_id_to_upgrade,
    &buffer_address,
    &dao_wallet,  // upgrade authority = DAO wallet
    &payer,
);
```

#### Token Transfer from Treasury

```rust
let transfer_ix = spl_token::instruction::transfer(
    &spl_token::id(),
    &dao_token_account,
    &recipient_token_account,
    &dao_wallet,  // authority = DAO wallet
    &[],
    amount,
)?;
```

#### Mint Tokens

```rust
let mint_ix = spl_token::instruction::mint_to(
    &spl_token::id(),
    &governed_mint,
    &destination_account,
    &dao_wallet,  // mint authority = DAO wallet
    &[],
    amount,
)?;
```

#### Call Any Program

You can create instructions for any program. The key insight is that the Governance PDA (DAO Wallet) acts as a signer, so it can authorize any action where it holds authority:

```rust
// Generic pattern for calling any program
let custom_ix = Instruction {
    program_id: target_program_id,
    accounts: vec![
        AccountMeta::new(dao_wallet, true),  // DAO wallet as signer
        // ... other accounts as needed
    ],
    data: your_instruction_data,
};
```

### Important Notes

* **DAO Wallet vs Governance PDA**: Always use the DAO Wallet (native treasury) as the authority over assets. It is a PDA with no data, derived from the Governance account, and owned by the System program. It behaves like a regular wallet.
* **hold\_up\_time**: Set this to add a delay between vote completion and execution. This gives the community time to react to controversial proposals.
* **Multiple transactions per option**: A proposal option can have multiple transactions that execute independently.
* **Execution is permissionless**: Once the vote passes and hold-up time elapses, anyone can trigger execution.
