Skip to content

ISSUE: Foundry Transitive Dependency Resolution Failures with LayerZero V2 #1627

@St0rmBr3w

Description

@St0rmBr3w

Problem Summary

Core Issue: forge install fails to resolve LayerZero V2's transitive dependencies, causing immediate compilation failures and forcing error-prone manual workarounds.

Secondary Issue: Conflicting package architecture with duplicate OApp implementations creates version authority confusion.

Impact: All Foundry projects integrating LayerZero V2 fail to build without manual intervention and face unclear dependency hierarchies.


Technical Root Cause

1. Missing Transitive Dependencies

LayerZero V2 contracts import external dependencies not resolved by Foundry:

// @layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol
import { BytesLib } from "solidity-bytes-utils/contracts/BytesLib.sol"; // ❌ Missing

Compilation Error:

Error (6275): Source "solidity-bytes-utils/contracts/BytesLib.sol" not found

2. Package Architecture Confusion

LayerZero V2 contains duplicate OApp packages with unclear hierarchy:

# Two different OApp implementations
lib/devtools/packages/oapp-evm/                    # "Latest" devtools version
lib/layerzero-v2/packages/layerzero-v2/evm/oapp/   # "Official" LayerZero V2 version

Remapping Ambiguity:

# Which one is canonical?
'@layerzerolabs/oapp-evm/=lib/devtools/packages/oapp-evm/'
# vs
'@layerzerolabs/oapp-evm/=lib/layerzero-v2/packages/layerzero-v2/evm/oapp/'

Affected Critical Components

1. OptionsBuilder Utility

// ❌ Fails to compile
import { OptionsBuilder } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol";

bytes memory options = OptionsBuilder.newOptions().addExecutorLzReadOption(100000, 128, 0);

Version Authority Problems:

// Same import path, different implementations
import { OptionsBuilder } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol";

// Could resolve to either:
// lib/devtools/packages/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol
// lib/layerzero-v2/packages/layerzero-v2/evm/oapp/contracts/oapp/libs/OptionsBuilder.sol

2. Configuration Structs

// ❌ Requires heavy imports for simple structs
import { ReadLibConfig } from "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/readlib/ReadLibBase.sol";
import { SetConfigParam } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol";
import { EnforcedOptionParam } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol";

3. Package Hierarchy Conflicts

Package Location Perceived Purpose Critical Issues
devtools/packages/oapp-evm/ Latest development code • Unclear stability guarantees
• Missing from main repo
• Dependency on devtools
layerzero-v2/evm/oapp/ Official stable release • May lack latest features
• Separate dependency tree
• Version sync issues

Solution Analysis & Cost-Benefit

Solution 1A: Manual Dependency Installation (Devtools Package)

git submodule add https://github.com/GNSPS/solidity-bytes-utils.git lib/solidity-bytes-utils
'@layerzerolabs/oapp-evm/=lib/devtools/packages/oapp-evm/'
Benefits Costs
• Uses official LayerZero utilities • Manual discovery required
• Type-safe operations • Project setup complexity
• Latest features available Version authority confusion
• Matches LayerZero documentation Extra dependency on devtools

Best Practice Compliance: ⚠️ Medium - Uses intended APIs but unclear package authority


Solution 1B: Manual Dependency Installation (Official V2 Package)

'@layerzerolabs/oapp-evm/=lib/layerzero-v2/packages/layerzero-v2/evm/oapp/'
Benefits Costs
• Clear version authority • Manual discovery required
• Official LayerZero source May lack latest features
• Stable release cycle Same dependency issues
• Single source of truth Potential feature lag

Best Practice Compliance: ✅ High - Clear authority, uses intended APIs


Solution 2: Manual Options Encoding

// Replace OptionsBuilder with manual encoding
bytes memory options = abi.encodePacked(
    uint16(3),       // TYPE_3 options
    uint8(1),        // EXECUTOR_WORKER_ID
    uint16(21),      // option size
    uint8(5),        // OPTION_TYPE_LZREAD
    uint128(100000), // gas
    uint32(128),     // size
    uint128(0)       // value
);
Benefits Costs
• Zero external dependencies • High error probability
• Full control over encoding • Magic numbers
• No import issues • Maintenance burden
Avoids package conflicts • Poor readability

Best Practice Compliance: ❌ Low - Violates DRY, maintainability, readability


Solution 3: Minimal Interface Package

// @custom/lz-interfaces/ILayerZeroTypes.sol
interface ILayerZeroTypes {
    struct ReadLibConfig {
        address executor;
        uint8 requiredDVNCount;
        uint8 optionalDVNCount;
        uint8 optionalDVNThreshold;
        address[] requiredDVNs;
        address[] optionalDVNs;
    }
}
Benefits Costs
• Lightweight dependencies • Struct maintenance overhead
• Clean imports • Type compatibility issues
• Fast compilation • Version sync complexity
Clear package ownership • ABI encoding assumptions

Best Practice Compliance: ⚠️ Medium - Good separation of concerns, but introduces maintenance risk


Solution 4: Automated Installation Script (Package-Aware)

#!/bin/bash
# install-lz-deps.sh
forge install layerzero-labs/LayerZero-v2 --no-commit
git submodule add https://github.com/GNSPS/solidity-bytes-utils.git lib/solidity-bytes-utils
# Auto-update foundry.toml with explicit package choice
Benefits Costs
• One-time setup • Script maintenance
• Reproducible builds • Platform compatibility
• Team consistency • Initial script development
Explicit package strategy • Must choose package hierarchy

Best Practice Compliance: ✅ High - Automation, reproducibility, documentation


Solution 5: Package Consolidation Strategy

# Clear dependency strategy - official packages only
forge install layerzero-labs/LayerZero-v2 --no-commit

# Use official V2 packages with clear versioning
'@layerzerolabs/oapp-evm/=lib/LayerZero-v2/packages/layerzero-v2/evm/oapp/'
'@layerzerolabs/devtools/=lib/LayerZero-v2/packages/devtools/'  # If needed separately
Benefits Costs
• Single source of truth • Migration effort for existing projects
• Clear version semantics • Potential feature restrictions
• Simplified dependency tree Requires LayerZero team changes
• Official support channel • Documentation updates needed

Best Practice Compliance: ✅ High - Clear separation of concerns, single authority


Recommended Solution

Primary: Package-Aware Automated Installation Script

Rationale: Addresses both dependency resolution and package hierarchy issues.

#!/bin/bash
# scripts/install-layerzero-v2.sh
set -e

echo "Installing LayerZero V2 with explicit package hierarchy..."

# Install official LayerZero V2 (single source of truth)
forge install layerzero-labs/LayerZero-v2 --no-commit
forge install OpenZeppelin/openzeppelin-contracts --no-commit

# Install missing transitive dependencies
git submodule add https://github.com/GNSPS/solidity-bytes-utils.git lib/solidity-bytes-utils

# Update foundry.toml with explicit package hierarchy
cat >> foundry.toml << 'EOF'

# LayerZero V2 Complete Setup - Official Packages Only
remappings = [
    # Use official LayerZero V2 packages (not devtools)
    '@layerzerolabs/oapp-evm/=lib/LayerZero-v2/packages/layerzero-v2/evm/oapp/',
    '@layerzerolabs/lz-evm-protocol-v2/=lib/LayerZero-v2/packages/layerzero-v2/evm/protocol/',
    '@layerzerolabs/lz-evm-messagelib-v2/=lib/LayerZero-v2/packages/layerzero-v2/evm/messagelib/',
    
    # Standard dependencies
    '@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/',
    'solidity-bytes-utils/=lib/solidity-bytes-utils/',
]
EOF

echo "✅ LayerZero V2 installation complete with official package hierarchy"
echo "📋 Using: LayerZero V2 official packages (not devtools)"

Alternative: Devtools Package Strategy

For projects requiring latest features:

# Alternative installation for devtools packages
cat >> foundry.toml << 'EOF'
# LayerZero V2 DevTools Setup - Latest Features
remappings = [
    # Use devtools packages for latest features
    '@layerzerolabs/oapp-evm/=lib/LayerZero-v2/packages/devtools/packages/oapp-evm/',
    '@layerzerolabs/lz-evm-protocol-v2/=lib/LayerZero-v2/packages/layerzero-v2/evm/protocol/',
    '@layerzerolabs/lz-evm-messagelib-v2/=lib/LayerZero-v2/packages/layerzero-v2/evm/messagelib/',
    
    # Standard dependencies
    '@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/',
    'solidity-bytes-utils/=lib/solidity-bytes-utils/',
]
EOF

Fallback: Manual Encoding Pattern

For minimal dependency projects:

library LayerZeroOptionsEncoder {
    /// @dev Encodes LayerZero read options following official specification
    /// @param gas Gas limit for read operation
    /// @param size Expected response data size in bytes
    /// @param value Native token value to send
    /// @return Encoded options bytes
    function encodeReadOptions(
        uint128 gas,
        uint32 size,
        uint128 value
    ) internal pure returns (bytes memory) {
        return abi.encodePacked(
            uint16(3),    // TYPE_3 options
            uint8(1),     // EXECUTOR_WORKER_ID
            uint16(21),   // option size (1 + 16 + 4 = 21 bytes)
            uint8(5),     // OPTION_TYPE_LZREAD
            gas,          // execution gas limit
            size,         // response data size
            value         // msg.value for execution
        );
    }
}

Architectural Recommendations

For LayerZero Team

  1. Consolidate Package Structure:

    layerzero-v2/
    ├── packages/layerzero-v2/evm/
    │   ├── oapp/           # Single canonical OApp package
    │   ├── protocol/       # Core protocol
    │   └── messagelib/     # Message libraries
    └── packages/devtools/  # Development utilities only (separate namespace)
    
  2. Clear Version Strategy:

    • Official stable packages in layerzero-v2/evm/
    • Development tools in separate @layerzerolabs/devtools namespace
    • Semantic versioning with clear stable/dev branches
  3. Dependency Declaration:

    # forge.deps.toml (proposed)
    [dependencies]
    "solidity-bytes-utils" = "^0.1.3"
    "@openzeppelin/contracts" = "^4.9.0"

For Developers (Immediate)

Recommended Import Strategy:

// Use explicit, version-aware imports with clear package choice
import { OptionsBuilder } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol";
import { ReadLibConfig } from "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/readlib/ReadLibBase.sol";

// Document package choice in comments
// Using: LayerZero V2 official packages for stability
// Alternative: DevTools packages for latest features

Implementation Requirements

Immediate Actions

  1. Create package-aware installation script with clear hierarchy choice
  2. Document package strategy in project README with trade-offs
  3. Provide fallback encoding utilities for minimal setups
  4. Establish package choice guidelines for teams

Best Practices Enforcement

  • Use explicit package hierarchy documentation
  • Implement proper error handling in manual encoding
  • Document all magic numbers and constants
  • Validate encoded options against official specifications
  • Choose and document package strategy consistently across team

Success Metrics

Metric Target Measurement
Setup time < 5 minutes Time from git clone to successful forge build
Error rate < 5% Failed integrations due to dependency issues
Package clarity 100% Projects have documented package strategy
Maintenance overhead < 1 hour/month Time spent on dependency updates

Severity: High
Priority: P1
Estimated Resolution: 3-5 days
Owner: Platform Team + LayerZero Architecture Team

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions