Skip to content

Migrate to native u256 and fix critical architectural bugs#38

Merged
ch4r10t33r merged 2 commits into
masterfrom
feature
Oct 9, 2025
Merged

Migrate to native u256 and fix critical architectural bugs#38
ch4r10t33r merged 2 commits into
masterfrom
feature

Conversation

@ch4r10t33r

@ch4r10t33r ch4r10t33r commented Oct 9, 2025

Copy link
Copy Markdown
Collaborator

🚀 Migrate to Native u256 and Fix Critical Bugs

This PR completes the migration from a custom U256 type to Zig's native u256, along with fixing critical architectural issues discovered during the migration.


✅ Changes Summary

1. Native u256 Migration

Replaced custom U256 struct with Zig's native u256 type throughout the codebase.

  • Created Ethereum utility functions:

    • u256FromBytes() - Convert from big-endian bytes (Ethereum format)
    • u256ToBytes() - Convert to big-endian bytes
    • u256FromHex() - Parse from hex string
    • u256ToHex() - Format as hex string
    • u256ToU64() - Safe conversion to u64
  • Updated all type definitions:

    • AbiValue now uses u256 instead of U256
    • Transaction gas prices and values use u256
    • Block difficulty and fees use u256
    • Receipt gas prices use u256
    • All RPC types use u256
  • Simplified arithmetic:

    • Old: value.add(fee) → New: value + fee
    • Old: balance.gte(required) → New: balance >= required
    • Old: amount.mulScalar(10) → New: amount * 10
  • Kept legacy U256 wrapper for backwards compatibility (thin wrapper around native u256)

2. Fixed Critical Architectural Bug (Issues #35, #37)

Problem: Dangling pointer in Provider structure causing segfaults and bus errors.

Root Cause:

// OLD (broken):
pub const Provider = struct {
    rpc_client: RpcClient,
    eth: EthNamespace,  // Holds pointer to rpc_client
};
// When Provider returns by value, it moves, making eth.client pointer invalid!

Solution: On-demand namespace creation

// NEW (fixed):
pub const Provider = struct {
    rpc_client: RpcClient,
    
    pub fn getEth(self: *Provider) EthNamespace {
        return EthNamespace.init(&self.rpc_client);  // Created fresh each time
    }
};

All Provider methods now create namespaces on-demand, eliminating dangling pointers.

3. Fixed Memory Leak in ABI Types (Issue #36)

Problem: AbiType.toString() allocated memory for constant strings that were never freed.

Solution: Return const string literals (no allocation needed)

// OLD:
.address => try allocator.dupe(u8, "address"),  // Allocates!

// NEW:
.address => "address",  // Const literal - no allocation!

4. Added JSON Value Cleanup

Created src/rpc/free_json.zig with freeJsonValue() function to properly clean up JSON values returned from RPC calls, preventing memory leaks in network operations.


📊 Files Changed (22 files)

Core Primitives & Types

  • src/primitives/uint.zig - New u256 utilities, legacy U256 wrapper
  • src/root.zig - Updated exports, deprecated U256
  • src/abi/types.zig - u256 in AbiValue, fixed toString()
  • src/abi/encode.zig - Native u256 encoding
  • src/abi/decode.zig - Native u256 decoding
  • src/abi/packed.zig - Packed encoding with u256
  • src/types/transaction.zig - All gas/value fields use u256
  • src/types/block.zig - Difficulty and fees use u256
  • src/types/receipt.zig - Gas price uses u256

RPC & Provider Layer

  • src/rpc/client.zig - Fixed JSON array handling
  • src/rpc/eth.zig - u256 return types, added defer cleanup
  • src/rpc/types.zig - All u256 types
  • src/rpc/free_json.zig - NEW: JSON cleanup utilities
  • src/providers/provider.zig - On-demand namespaces, u256 methods
  • src/providers/http.zig - Pointer-based methods
  • src/rlp/packed.zig - u256 byte conversion

Middleware

  • src/middleware/gas.zig - Native u256 arithmetic, fixed provider access

Examples

  • examples/02_query_blockchain.zig - Format specifiers for u256
  • examples/03_send_transaction.zig - Format specifiers, native u256
  • examples/05_transaction_receipts.zig - Native u256 arithmetic
  • examples/07_complete_workflow.zig - Format specifiers
  • run_all_examples.sh - NEW: Helper script

✅ Testing & Verification

All Checks Pass

zig build test       # ✅ All unit tests pass
zig build           # ✅ Clean build
zig build lint      # ✅ Code formatting correct
zig build -Dexamples examples  # ✅ All 7 examples build and run

Examples Status

  • 01_wallet_creation - Runs successfully
  • 02_query_blockchain - RPC calls work (was crashing, now fixed)
  • 03_send_transaction - Middleware works (was crashing, now fixed)
  • 04_smart_contracts - No memory leaks (was leaking, now fixed)
  • 05_transaction_receipts - Runs successfully
  • 06_event_monitoring - Runs successfully
  • 07_complete_workflow - Runs successfully

💡 Benefits

Code Quality

  • ~200 lines removed: Eliminated custom arithmetic implementations
  • Simpler: Direct use of +, -, *, /, ==, <, > operators
  • Safer: Fixed critical dangling pointer bug
  • Cleaner: No unnecessary memory allocations

Performance

  • Better optimizations: Compiler can optimize native u256 better than custom type
  • Less overhead: No wrapper struct indirection
  • Faster arithmetic: Native operations vs custom implementations

Maintainability

  • Standard type: Uses Zig's built-in 256-bit integers
  • Less code: Fewer custom implementations to maintain
  • Better architecture: On-demand namespace creation is cleaner design

🐛 Issues Resolved

Closes #35 - Bus error in RPC client (dangling pointer)
Closes #36 - Memory leak in AbiType.toString()
Closes #37 - Segmentation fault in middleware (dangling pointer)


🔗 Related Issues

Account Abstraction (ERC-4337) roadmap created: Issues #27-34


📝 Migration Notes

For Users

The API remains mostly compatible. If you were using U256:

Old way:

const value = U256.fromInt(1_000_000_000_000_000_000);
const sum = value.add(fee);
if (balance.gte(required)) { ... }

New way:

const value: u256 = 1_000_000_000_000_000_000;
const sum = value + fee;
if (balance >= required) { ... }

For Ethereum-specific conversions, use the utility functions:

const bytes = zigeth.primitives.u256ToBytes(value);
const hex = try zigeth.primitives.u256ToHex(value, allocator);

The legacy U256 wrapper is still available for backwards compatibility.


🧪 Testing

Tested on:

  • Platform: macOS ARM64 (Apple Silicon)
  • Zig Version: 0.14.1
  • Networks: Ethereum Mainnet, Sepolia Testnet (via Etherspot RPC)

All core functionality verified working with native u256.

@ch4r10t33r ch4r10t33r changed the title fix: Fixed segmentation faults Migrate to native u256 and fix critical architectural bugs Oct 9, 2025
@ch4r10t33r
ch4r10t33r merged commit e4b3ec2 into master Oct 9, 2025
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant