V2 Improvements
| v1 | v2 | |
|---|---|---|
| Merkle tree type | Concurrent | Batched |
| State tree depth | 26 (~67M leaves) | 32 (~4B leaves) |
| Address tree depth | 26 | 40 |
| Address tree accounts | Separate tree + queue | Single batch tree |
- TypeScript
- Rust
- Pinocchio
- AI Prompt
Update imports
Report incorrect code
Copy
Ask AI
// v1
import {
deriveAddress,
deriveAddressSeed,
defaultTestStateTreeAccounts,
PackedAccounts,
} from "@lightprotocol/stateless.js";
Report incorrect code
Copy
Ask AI
// v2
import {
deriveAddressV2,
deriveAddressSeedV2,
batchAddressTree,
PackedAccounts,
featureFlags,
VERSION,
} from "@lightprotocol/stateless.js";
Enable v2 mode
Set the feature flag before making any calls:Report incorrect code
Copy
Ask AI
import { featureFlags, VERSION } from "@lightprotocol/stateless.js";
(featureFlags as any).version = VERSION.V2;
Update address derivation
The seed and address derivation functions have different signatures in v2:Report incorrect code
Copy
Ask AI
// v1 - Program ID passed to seed derivation
const seed = deriveAddressSeed(
[counterSeed, signer.publicKey.toBytes()],
new PublicKey(program.idl.address)
);
const address = deriveAddress(seed, addressTree);
Report incorrect code
Copy
Ask AI
// v2 - Program ID passed to address derivation
const seed = deriveAddressSeedV2([counterSeed, signer.publicKey.toBytes()]);
const address = deriveAddressV2(
seed,
addressTree,
new PublicKey(program.idl.address)
);
Update address tree references
UsebatchAddressTree instead of defaultTestStateTreeAccounts().addressTree:Report incorrect code
Copy
Ask AI
// v1
const addressTree = defaultTestStateTreeAccounts().addressTree;
const addressQueue = defaultTestStateTreeAccounts().addressQueue;
// In proof request
{
tree: addressTree,
queue: addressQueue,
address: bn(address.toBytes()),
}
Report incorrect code
Copy
Ask AI
// v2 - queue equals tree for batch trees
const addressTree = new PublicKey(batchAddressTree);
// In proof request
{
tree: addressTree,
queue: addressTree,
address: bn(address.toBytes()),
}
Update PackedAccounts
Use the v2 variant when building remaining accounts:Report incorrect code
Copy
Ask AI
// v1
const remainingAccounts = PackedAccounts.newWithSystemAccounts(systemAccountConfig);
Report incorrect code
Copy
Ask AI
// v2
const remainingAccounts = PackedAccounts.newWithSystemAccountsV2(systemAccountConfig);
Update Cargo.toml
Add thev2 feature flag to your dependencies:Report incorrect code
Copy
Ask AI
[dependencies]
light-sdk = { version = "0.16", features = ["v2"] }
light-sdk-types = { version = "0.3", features = ["v2"] }
Update imports
Report incorrect code
Copy
Ask AI
// v1
use light_sdk::{
address::v1::derive_address,
constants::ADDRESS_TREE_V1,
cpi::v1::{CpiAccounts, LightSystemProgramCpi},
};
Report incorrect code
Copy
Ask AI
// v2
use light_sdk::{
address::v2::derive_address,
cpi::v2::{CpiAccounts, LightSystemProgramCpi},
};
use light_sdk_types::ADDRESS_TREE_V2;
Update address derivation
Thederive_address function signature remains the same, but the internal derivation logic differs. No code changes needed beyond updating the import path.Update address params
Replaceinto_new_address_params_packed() with into_new_address_params_assigned_packed():Report incorrect code
Copy
Ask AI
// v1
let new_address_params = instruction_data
.address_tree_info
.into_new_address_params_packed(address_seed);
Report incorrect code
Copy
Ask AI
// v2
let new_address_params = instruction_data
.address_tree_info
.into_new_address_params_assigned_packed(address_seed, Some(0));
Some(0) to assign the address to the first available queue.Update tree validation
If your program validates the address tree pubkey, update the constant:Report incorrect code
Copy
Ask AI
// v1
if address_tree_pubkey.to_bytes() != ADDRESS_TREE_V1 {
return Err(ProgramError::InvalidAccountData);
}
Report incorrect code
Copy
Ask AI
// v2
if address_tree_pubkey.to_bytes() != ADDRESS_TREE_V2 {
return Err(ProgramError::InvalidAccountData);
}
The Pinocchio SDK follows the same import changes as the Rust SDK, plus one additional API change.
Update imports
Report incorrect code
Copy
Ask AI
// v1
use light_sdk_pinocchio::{
address::v1::derive_address,
constants::ADDRESS_TREE_V1,
cpi::v1::{CpiAccounts, LightSystemProgramCpi},
};
Report incorrect code
Copy
Ask AI
// v2
use light_sdk_pinocchio::{
address::v2::derive_address,
cpi::v2::{CpiAccounts, LightSystemProgramCpi},
};
use light_sdk_types::ADDRESS_TREE_V2;
Update CpiAccounts
CpiAccounts::try_new_with_config() becomes CpiAccounts::new_with_config() (infallible):Report incorrect code
Copy
Ask AI
// v1
let cpi_accounts = CpiAccounts::try_new_with_config(signer, &accounts[1..], config)
.map_err(to_custom_error_u32)?;
Report incorrect code
Copy
Ask AI
// v2
let cpi_accounts = CpiAccounts::new_with_config(signer, &accounts[1..], config);
Update address params
Same as the Rust SDK:Report incorrect code
Copy
Ask AI
// v1
let new_address_params = instruction_data
.address_tree_info
.into_new_address_params_packed(address_seed);
Report incorrect code
Copy
Ask AI
// v2
let new_address_params = instruction_data
.address_tree_info
.into_new_address_params_assigned_packed(address_seed, Some(0));
Migrate this Light Protocol program from v1 to v2 Merkle trees.
Goal
Produce a fully working migration that builds and tests pass.Available commands
Via Bash tool:cargo build-sbf,cargo test-sbf,cargo fmt,cargo clippyanchor build,anchor testgrep,sed
Documentation
- Migration Guide: https://zkcompression.com/references/migration-v1-to-v2
- Reference PR: https://github.com/Lightprotocol/program-examples/commit/54f0e7f15c2972a078f776cfb40b238d83c7e486
Reference repos
program-examples/counter/anchor/ ├── programs/counter/src/lib.rs # v2 patterns: derive_address, CpiAccounts ├── Cargo.toml # v2 feature flags └── tests/counter.ts # v2 client patternsWorkflow
Phase 1: Index program
Find all v1 patterns:grep -r “::v1::” src/ tests/ grep -r “ADDRESS_TREE_V1” src/ grep -r “into_new_address_params_packed” src/ grep -r “get_address_tree_v1” tests/Phase 2: Update dependencies
Add v2 feature to Cargo.toml:[dependencies] light-sdk = light-sdk-types = [dev-dependencies] light-program-test = light-client =Phase 3: Rust SDK replacements
| v1 Pattern | v2 Replacement |
|---|---|
| address::v1::derive_address | address::v2::derive_address |
| cpi::v1::CpiAccounts | cpi::v2::CpiAccounts |
| cpi::v1::LightSystemProgramCpi | cpi::v2::LightSystemProgramCpi |
| constants::ADDRESS_TREE_V1 | light_sdk_types::ADDRESS_TREE_V2 |
| .into_new_address_params_packed(seed) | .into_new_address_params_assigned_packed(seed, Some(0)) |
| .add_system_accounts(config) | .add_system_accounts_v2(config) |
Phase 4: TypeScript SDK replacements
| v1 Pattern | v2 Replacement |
|---|---|
| deriveAddress( | deriveAddressV2( |
| deriveAddressSeed( | deriveAddressSeedV2( |
| defaultTestStateTreeAccounts().addressTree | batchAddressTree |
| .newWithSystemAccounts( | .newWithSystemAccountsV2( |
| get_address_tree_v1() | get_address_tree_v2() |
| get_random_state_tree_info_v1() | get_random_state_tree_info() |
Phase 5: Build and test loop
Required commands (no shortcuts):For Anchor programs:anchor build && anchor testFor Native programs: cargo build-sbf && cargo test-sbfNO shortcuts allowed:- Do NOT use
cargo build(must usecargo build-sbf) - Do NOT use
cargo test(must usecargo test-sbf) - Tests MUST run against real BPF bytecode
- Each debugger gets fresh context + previous debug reports
- Each attempt tries something DIFFERENT
- NEVER GIVE UP - keep spawning until fixed