Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
e159cfd
add test suite
M-Daeva Sep 5, 2025
f583062
added token-2022-caller
M-Daeva Sep 5, 2025
c31ab58
resolved version conflicts
M-Daeva Sep 6, 2025
e8b860c
added token_try_initialize_mint
M-Daeva Sep 6, 2025
6b8a097
fixed initialize_mint
M-Daeva Sep 6, 2025
bd09eaf
fixed initialize_mint accounts
M-Daeva Sep 6, 2025
2a4529c
added additional_accounts in token_try_initialize_mint
M-Daeva Sep 7, 2025
386ef6f
added token_try_create_mint_account
M-Daeva Sep 7, 2025
be8b271
added token_2022_try_initialize_mint, token_2022_query_mint_state
M-Daeva Sep 7, 2025
3ed586d
added token_2022_proxy_query_mint_state
M-Daeva Sep 7, 2025
2007eb7
fixed naming
M-Daeva Sep 7, 2025
d45823e
updated legacy deps
M-Daeva Sep 7, 2025
0b8ef8c
removed unused code, moved test.sh
M-Daeva Sep 7, 2025
683bfa6
updated project structure
M-Daeva Sep 7, 2025
8cd6dc9
added test setup readme
M-Daeva Sep 7, 2025
237ad7f
fixed diagram padding
M-Daeva Sep 7, 2025
e11cf60
updated diagram
M-Daeva Sep 7, 2025
16a3c2e
Merge pull request #6 from M-Daeva/mdaeva/tests
zubayr1 Sep 9, 2025
8d96727
added Token2022TokenMetadataExtension
M-Daeva Sep 9, 2025
e13cbf0
added Token2022TokenGroupExtension
M-Daeva Sep 9, 2025
2dfd19e
fixed token_2022_try_initialize_token_group, token_2022_query_token_g…
M-Daeva Sep 9, 2025
627fad8
updated initialize_group_pointer_with_token_group
M-Daeva Sep 10, 2025
3d27ccf
added group_pointer
M-Daeva Sep 10, 2025
ff82098
added group_pointer::initialize
M-Daeva Sep 10, 2025
8ef94ba
fixed initialize_instruction_data
M-Daeva Sep 10, 2025
0eadbd2
added i::group_pointer::update
M-Daeva Sep 11, 2025
47de4c1
fixed initialize_instruction_data
M-Daeva Sep 11, 2025
62bee1a
fixed Update signers type
M-Daeva Sep 11, 2025
10fa4e2
added GroupPointer
M-Daeva Sep 11, 2025
0150044
added GroupPointer::from_account_info checks in initialize
M-Daeva Sep 11, 2025
02c91ce
chaged mod visibility
M-Daeva Sep 11, 2025
3dd1f3f
removed unused code
M-Daeva Sep 11, 2025
34c5d13
removed token group code from group pointer
M-Daeva Sep 11, 2025
63f6e13
added Token2022TokenGroupExtension
M-Daeva Sep 11, 2025
31b2e25
added token_group extension
M-Daeva Sep 12, 2025
f6160e2
added InitializeGroup
M-Daeva Sep 12, 2025
82b3d7f
added update_max_size, update_group_authority, initialize_member
M-Daeva Sep 12, 2025
1726095
added group_member_pointer
M-Daeva Sep 12, 2025
dc39760
added target selector in test handlers
M-Daeva Sep 12, 2025
0f62b27
added Token2022GroupMemberPointerExtension
M-Daeva Sep 13, 2025
5215890
added token_group_member tests
M-Daeva Sep 14, 2025
14c2b16
added group_member_pointer tests
M-Daeva Sep 14, 2025
4cef8a8
added multisig handlers
M-Daeva Sep 15, 2025
57a8138
Merge pull request #14 from M-Daeva/mdaeva/extensions-0
zubayr1 Sep 16, 2025
242addb
added initialize_permanent_delegate
M-Daeva Sep 16, 2025
1e0424c
implemented templates structure
M-Daeva Sep 24, 2025
e0efe38
Merge pull request #15 from M-Daeva/mdaeva/permanent-delegate
zubayr1 Sep 30, 2025
d008647
add cpi guard invoke call
zubayr1 Sep 3, 2025
2c55683
add cpi extension
zubayr1 Sep 9, 2025
43911f1
add extension files
zubayr1 Sep 10, 2025
8889d3c
add tests
zubayr1 Sep 16, 2025
f937cde
fixed ui amount and cpi guards
M-Daeva Sep 17, 2025
af13c47
add pausable tests
zubayr1 Sep 25, 2025
92be110
add default account state tests
zubayr1 Sep 30, 2025
9b28834
feat(extension): add MetadataPointer variant to ExtensionDiscriminato…
burhankhaja Oct 1, 2025
a3c2f0d
feat(extension): implement MetadataPointer extension
burhankhaja Oct 2, 2025
4afda89
refactor(extension): expose module in extension scope
burhankhaja Oct 2, 2025
2d460e7
feat(proxy): add metadata_pointer instruction module
burhankhaja Oct 3, 2025
ac1178b
chore(proxy): register metadata_pointer instruction in mod.rs
burhankhaja Oct 3, 2025
f56b84e
feat(proxy): handle metadata_pointer entrypoint
burhankhaja Oct 3, 2025
a675a8e
test(helper): add MetadataPointer test helper module
burhankhaja Oct 4, 2025
de116bb
test(metadata): implement MetadataPointer tests
burhankhaja Oct 4, 2025
e66cee2
refactor(tests): expose MetadataPointer in lib.rs
burhankhaja Oct 4, 2025
ecc8f2d
add transfer hook and interest bearning mint
zubayr1 Oct 4, 2025
05a1bdc
fixed tests
M-Daeva Oct 4, 2025
154e049
test(group_pointer): assert Proxy target's state after clearing group…
burhankhaja Oct 5, 2025
4a7812e
Merge pull request #19 from M-Daeva/transfer-hook-and-interest-bearing
zubayr1 Oct 5, 2025
6217d7f
feat(extension): add MemoTransfer variant to ExtensionDiscriminator enum
burhankhaja Oct 6, 2025
8e4d669
feat(extension): implement MemoTransfer extension
burhankhaja Oct 6, 2025
a117340
chore(extention): expose memo_transfer module in extention scope
burhankhaja Oct 6, 2025
88ba864
tests(helpers): add token_account helper
burhankhaja Oct 8, 2025
e772fa7
test(token_account): add SPL & Proxy token_account tests (Proxy test …
burhankhaja Oct 8, 2025
14fdce4
feat(proxy): add token_account proxy code
burhankhaja Oct 9, 2025
4125c8a
feat(proxy): add memo_transfer proxy code
burhankhaja Oct 9, 2025
5dcbc91
test(helpers): add memo_transfer helper code
burhankhaja Oct 10, 2025
178160b
test(memo_transfer): add memo_transfer Target::Spl tests
burhankhaja Oct 10, 2025
485f0f4
test(metadata-pointer) : assert Target::Proxy state in proxy_initiali…
burhankhaja Oct 12, 2025
a173b49
fix(test): fix proxy_initialize_and_update_metadata_pointer_with_mult…
burhankhaja Oct 12, 2025
38f2cb6
cleanup: removed obsolete TODO notes from proxy_create_and_initialize…
burhankhaja Oct 13, 2025
cf3bf3d
Add files via upload
zubayr1 Oct 13, 2025
eac50a0
fix(memo_transfer_proxy): use correct instruction_data index after ex…
burhankhaja Oct 16, 2025
9fbadff
test(memo_transfer): Add complete Target::Proxy tests
burhankhaja Oct 16, 2025
1343dee
Merge pull request #21 from burhankhaja/group-pointer-fix
zubayr1 Oct 16, 2025
15242d7
Merge pull request #18 from burhankhaja/metadata-pointer
zubayr1 Oct 19, 2025
f9b27fb
Merge branch 'main' into memo-transfer
burhankhaja Oct 20, 2025
f684659
Merge pull request #22 from burhankhaja/memo-transfer
zubayr1 Oct 21, 2025
32602fd
Merge branch 'main' into zubayr/extensions
burhankhaja Oct 24, 2025
e9fe5d5
Merge pull request #9 from Turbin3/zubayr/extensions
burhankhaja Oct 27, 2025
d4336f0
Delete EXTENSIONS_STATUS.md
burhankhaja Oct 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,002 changes: 5,925 additions & 77 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ members = [
"sdk/log/macro",
"sdk/pinocchio",
"sdk/pubkey",
"cpi-tests/programs/*",
"cpi-tests/tests",
]

[workspace.package]
Expand All @@ -23,6 +25,9 @@ five8_const = "0.1.4"
pinocchio = { version = "0.9", path = "sdk/pinocchio" }
pinocchio-log-macro = { version = "0.5", path = "sdk/log/macro" }
pinocchio-pubkey = { version = "0.3", path = "sdk/pubkey" }
pinocchio-token = { version = "0.4.0", path = "./programs/token" }
pinocchio-token-2022 = { version = "0.1.0", path = "./programs/token-2022" }
token-2022-proxy = { version = "1.0.0", path = "./cpi-tests/programs/token-2022-proxy" }
quote = "1.0"
regex = "1"
syn = "1.0"
Expand Down
215 changes: 215 additions & 0 deletions cpi-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# Pinocchio Interface Test Setup for SPL Token-2022
Copy link
Collaborator

@febo febo Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cpi-tests should under the token-2022 folder since it is specific to it.


A comprehensive testing framework for validating the Pinocchio interface implementation against the SPL Token-2022 program, enabling developers to contribute confidently to the Pinocchio interface repository.

## Problem Statement

Before this test setup, the Pinocchio interface lacked comprehensive testing coverage, which meant:

- **Late Error Discovery**: Interface implementation errors were only discovered when used in downstream projects
- **No Validation Framework**: Contributors had no way to verify their interface implementations worked correctly
- **Manual Testing Burden**: Developers had to manually construct instructions and test scenarios
- **Inconsistent Testing**: No standardized approach for testing Cross-Program Invocations (CPI) and state reading functionality

This test setup solves these problems by providing a complete, automated testing environment that validates both the interface implementation and its real-world usage patterns.

## Architecture Overview

![Architecture Diagram](./diagrams/pinocchio_cpi_test_setup_architecture.drawio.svg)

The test setup consists of four main components:

### 1. Token-2022 Program
- **Purpose**: The actual SPL Token-2022 program implementation using Pinocchio interface
- **Key Features**:
- Implements token operations via Pinocchio interface
- Handles state validation and initialization checks
- Serves as the target program for CPI calls

### 2. Token-2022-Proxy Program
- **Purpose**: Demonstrates real-world usage of Pinocchio interface for CPI calls
- **Function**: Acts as an intermediary that executes Token-2022 operations through CPI
- **Why Important**: Simulates how other programs would integrate with Pinocchio interface in production

### 3. Target-Based Execution System
- **Purpose**: Unified system for switching between SPL and Proxy program execution
- **Benefits**:
- Single interface for testing both execution paths
- Eliminates code duplication
- Provides consistent testing patterns

### 4. Test Functions
- **Design**: Functions that handle both SPL and Proxy execution based on `Target` parameter
- **Purpose**: Execute Token-2022 operations and query state through either interface
- **Advantages**:
- Simplified test creation without manual instruction building
- Easy state reading using both SPL and Pinocchio interfaces
- Consistent behavior across different instructions

## Code Structure

### Target Enum

The `Target` enum controls which execution path and interface to use:

```rust
/// Switch between SPL and Proxy programs
pub enum Target {
/// Execute token-2022 instruction directly, read state using SPL interface
Spl,
/// Execute token-2022 instruction using proxy program, read state using Pinocchio interface
Proxy,
}
```

### Trait Organization
Each Token-2022 instruction has its own trait with unified handlers:

```rust
pub trait Token2022InitializeMintExtension {
// Account management
fn token_2022_try_create_mint_account(
&mut self,
sender: AppUser,
mint: Option<Keypair>,
extensions: Option<&[ExtensionType]>,
) -> TestResult<(TransactionMetadata, Keypair)>;

// Unified execution (Target determines SPL vs Proxy)
fn token_2022_try_initialize_mint(
&mut self,
target: Target,
sender: AppUser,
mint: &Pubkey,
decimals: u8,
mint_authority: &Pubkey,
freeze_authority: Option<&Pubkey>,
) -> TestResult<TransactionMetadata>;

// Unified state reading (Target determines interface)
fn token_2022_query_mint(&self, target: Target, mint: &Pubkey) -> TestResult<Mint>;
}
```

### Test Categories

**1. Direct SPL Tests**
```rust
#[test]
fn initialize_mint() -> TestResult<()> {
// Tests direct Token-2022 program execution
app.token_2022_try_initialize_mint(Target::Spl, ...)?;
assert_eq!(app.token_2022_query_mint(Target::Spl, mint)?, expected_state);
}
```

**2. Proxy/CPI Tests**
```rust
#[test]
fn proxy_initialize_mint() -> TestResult<()> {
// Tests Pinocchio interface through proxy program
app.token_2022_try_initialize_mint(Target::Proxy, ...)?;
assert_eq!(app.token_2022_query_mint(Target::Proxy, mint)?, expected_state);
}
```

## Getting Started

### Prerequisites
- Rust toolchain with Solana program development setup
- LiteSVM testing environment

### Building and Testing

```bash
# Build and test with detailed output
./test.sh s

# Build and test (standard output)
./test.sh
```

### Adding New Instruction Tests

1. **Create a new trait** for your instruction:
```rust
pub trait Token2022YourInstructionExtension {
fn token_2022_try_your_instruction(
&mut self,
target: Target,
// ... other parameters
) -> TestResult<TransactionMetadata>;

fn token_2022_query_your_state(
&self,
target: Target,
account: &Pubkey,
) -> TestResult<YourState>;
}
```

2. **Implement the trait** following the established patterns:
- Use `Target` to determine execution path and state reading interface
- Handle both SPL direct execution and proxy CPI calls
- Implement state query methods for both interfaces

3. **Create test file** with both direct and proxy tests:
- Test the instruction execution with both targets
- Verify state changes
- Test error conditions
- Validate CPI behavior

## Testing Strategy

### What Gets Tested

**Interface Correctness**
- CPI calls work as expected
- State reading returns correct data
- Error handling behaves properly

**Implementation Validation**
- Pinocchio interface matches SPL interface behavior
- State structures are correctly mapped
- Account validation logic is consistent

**Integration Testing**
- Proxy programs can successfully call Token-2022 via Pinocchio
- Cross-program invocations maintain state consistency
- Complex instruction sequences work correctly

### Test Patterns

1. **Setup Phase**: Create necessary accounts and initial state
2. **Execution Phase**: Test both `Target::Spl` and `Target::Proxy` execution
3. **Validation Phase**: Compare state using both SPL and Pinocchio interfaces
4. **Edge Cases**: Test error conditions and boundary cases

## Contributing

When contributing to the Pinocchio interface:

1. **Add Tests First**: Create comprehensive tests for any new interface implementations
2. **Follow Patterns**: Use the established trait and testing patterns with `Target` enum
3. **Test Both Paths**: Always test both direct SPL and proxy execution
4. **Validate State**: Ensure state reading works correctly through Pinocchio interface
5. **Document Edge Cases**: Add tests for error conditions and special cases

## Benefits for Contributors

- **Confidence**: Know your interface implementation works before integration
- **Fast Feedback**: Catch errors immediately during development
- **Standardized Testing**: Consistent patterns across all instructions
- **Real-world Validation**: Test actual CPI usage scenarios
- **Easy Debugging**: Clear test structure makes issues easy to identify and fix

## Future Extensions

This framework can be extended to test:
- Additional SPL Token-2022 instructions
- Token-2022 extensions
- Complex multi-instruction scenarios
- Performance characteristics
- Error recovery patterns

By using this test setup, contributors can ensure their Pinocchio interface implementations are robust, correct, and ready for production use.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions cpi-tests/programs/token-2022-proxy/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "token-2022-proxy"
version = "1.0.0"
description = "A program to test Token-2022 Pinocchio CPI"
repository = { workspace = true }
edition = { workspace = true }
license = { workspace = true }

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = []
std = []

[dependencies]
pinocchio = { workspace = true }
pinocchio-log-macro = { workspace = true }
pinocchio-pubkey = { workspace = true }
pinocchio-token-2022 = { workspace = true }
spl-token-group-interface = "0.7.1"
spl-token-2022-interface = "2.0.0"
solana-program-option = "3.0.0"
solana-address = "1.0.0"
spl-pod = "0.7.1"
23 changes: 23 additions & 0 deletions cpi-tests/programs/token-2022-proxy/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use {
pinocchio::{log::sol_log, program_error::ProgramError, pubkey::Pubkey},
solana_program_option::COption,
spl_pod::optional_keys::OptionalNonZeroPubkey,
};

pub type Result<T> = core::result::Result<T, ProgramError>;

pub fn from_c_option<T>(data: COption<T>) -> Option<T> {
if data.is_some() {
Some(data.unwrap())
} else {
None
}
}

pub fn from_optional_non_zero_pubkey(address: OptionalNonZeroPubkey) -> Option<Pubkey> {
Option::<solana_address::Address>::from(address).map(|x| x.to_bytes())
}

pub fn show<T: core::fmt::Debug>(label: &str, data: T) {
sol_log(&format!("✅ {}: {:?}", label, data));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use {
pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult},
pinocchio_token_2022::extension::cpi_guard,
};

pub fn disable_guard(accounts: &[AccountInfo], _instruction_data: &[u8]) -> ProgramResult {
// The accounts should be: [account, owner, ...signers, token_program]

if accounts.len() < 4 {
Err(ProgramError::NotEnoughAccountKeys)?
}

let token_program = accounts.last().unwrap(); // token_program is always last
let account = &accounts[0];
let owner = &accounts[1];
let signers = &accounts[2..accounts.len() - 1]; // everything between owner and token_program

cpi_guard::DisableCpiGuard {
token_account: account,
owner,
signers,
token_program: &token_program.key(),
}
.invoke()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use {
pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult},
pinocchio_token_2022::extension::cpi_guard,
};

pub fn enable_guard(accounts: &[AccountInfo], _instruction_data: &[u8]) -> ProgramResult {
// The accounts should be: [account, owner, ...signers, token_program]

if accounts.len() < 4 {
Err(ProgramError::NotEnoughAccountKeys)?
}

let token_program = accounts.last().unwrap(); // token_program is always last
let account = &accounts[0];
let owner = &accounts[1];
let signers = &accounts[2..accounts.len() - 1]; // everything between owner and token_program

{
let acc = account.try_borrow_data()?;
let state = pinocchio_token_2022::extension::cpi_guard::state::CpiGuard::from_bytes(&acc)?;

if state.lock_cpi() {
return Ok(());
}
}

cpi_guard::EnableCpiGuard {
token_account: account,
owner,
signers,
token_program: &token_program.key(),
}
.invoke()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod enable_guard;
mod disable_guard;

pub use enable_guard::*;
pub use disable_guard::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use {
pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult},
pinocchio_token_2022::extension::default_account_state::instructions::initialize::InitializeDefaultAccountState,
spl_token_2022_interface::extension::default_account_state::instruction::decode_instruction,
};

pub fn initialize(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
let [mint, token_program] = accounts else {
Err(ProgramError::InvalidAccountData)?
};

let (_, state) = decode_instruction(instruction_data)
.map_err(|_| ProgramError::InvalidInstructionData)?;

if pinocchio_token_2022::state::Mint::from_account_info(mint)?.is_initialized() {
let config = pinocchio_token_2022::extension::default_account_state::state::DefaultAccountStateConfig::from_account_info(mint)?;

if config.state() != state as u8 {
Err(ProgramError::InvalidAccountData)?
}

return Ok(());
}

InitializeDefaultAccountState {
mint_account: mint,
state: state as u8,
token_program: &token_program.key(),
}
.invoke()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod initialize;
pub mod update;

pub use initialize::*;
pub use update::*;
Loading