Skip to content

Conversation

@sudo-owen
Copy link
Collaborator

Summary

This PR refactors the move metadata extraction pipeline by migrating it from a standalone client-side script to the Solidity transpiler, and introduces a dependency injection container for managing contract instances.

Key Changes

Metadata Extraction Migration

  • Removed client/scripts/extract-move-metadata.ts - the standalone 500-line Solidity parser script
  • Removed extract-metadata npm script from client/package.json
  • Removed pre-generated client/generated/move-metadata.json file
  • Added metadata extraction directly to the transpiler pipeline via MetadataExtractor class
  • Added DependencyManifest class to collect and organize contract metadata
  • Updated loadMoveMetadata() JSDoc to reflect that it now accepts data from the transpiler's dependency-manifest.json or other sources

Transpiler Enhancements

  • Added --emit-metadata flag to generate dependency manifest and factory functions
  • Added --metadata-only flag to output only metadata without TypeScript generation
  • Added MetadataExtractor class that parses ASTs to extract:
    • Constructor dependencies with type information
    • Contract constants and public methods
    • Move-specific properties (for IMoveSet implementations)
    • Effect metadata (for IEffect implementations)
  • Added ContractMetadata and ContractDependency dataclasses for structured metadata
  • Added write_metadata() method to output dependency-manifest.json and factories.ts
  • Added get_metadata_json() method for programmatic access to metadata

Dependency Injection Container

  • Added ContractContainer class to transpiler/runtime/index.ts with support for:
    • Singleton instance registration and resolution
    • Factory function registration with automatic dependency injection
    • Lazy singleton creation
    • Circular dependency detection
    • Child container creation for scoped instances
    • Bulk registration from dependency manifests
  • Added ContractFactory type for factory function definitions
  • Added globalContainer instance for convenience

Performance Optimization

  • Added build_qualified_name_cache() method to TypeRegistry for optimized type name lookups
  • Added _qualified_name_cache to CodeGenerator to avoid repeated set membership checks during code generation
  • Caches are built per-file based on current file type context

Implementation Details

The metadata extraction now happens during transpilation, eliminating the need for a separate extraction step. The transpiler can optionally emit:

  1. dependency-manifest.json - Complete contract metadata including dependencies, constants, and move properties
  2. factories.ts - Auto-generated factory functions and container setup for dependency injection

The dependency injection container provides a clean way to manage contract instances and their dependencies, supporting both singleton and factory patterns with automatic resolution.

…tion

Optimizations:
- Add qualified name cache to TypeRegistry for O(1) lookups
- Pre-build qualified name lookup dict per file during generation
- Reduces repeated set membership checks in get_qualified_name()

Metadata Emission (--emit-metadata, --metadata-only flags):
- Add ContractMetadata and ContractDependency dataclasses
- MetadataExtractor class extracts contract info from AST:
  - Dependencies from constructor parameters
  - Constants and move properties
  - Public methods and inheritance info
- DependencyManifest tracks all contracts and generates:
  - dependency-manifest.json with full contract metadata
  - factories.ts with factory functions for DI

Dependency Injection Container (runtime):
- Add ContractContainer class with:
  - registerSingleton() for shared instances
  - registerFactory() for per-resolve instances
  - registerLazySingleton() for deferred singletons
  - resolve() with automatic dependency resolution
  - Circular dependency detection
- Add globalContainer instance
- Enables automatic wiring of contract dependencies

This replaces the brittle move-metadata.json extraction with
transpiler-integrated metadata emission, ensuring consistency
between generated code and metadata.

https://claude.ai/code/session_01A35YqXXpYDgQaHzKgfkCEi
- Delete client/scripts/extract-move-metadata.ts (481 lines)
- Delete client/generated/move-metadata.json
- Remove extract-metadata npm script from client/package.json
- Update comment in metadata-converter.ts

The transpiler now handles metadata extraction via --emit-metadata flag,
which generates dependency-manifest.json with equivalent information
plus dependency graphs for automatic contract wiring.

https://claude.ai/code/session_01A35YqXXpYDgQaHzKgfkCEi
…tegration

Major documentation updates:
- Add new "Metadata and Dependency Injection" section
- Document --emit-metadata and --metadata-only CLI flags
- Document ContractContainer API and usage patterns
- Update Angular integration to use DI container
- Update architecture diagram to show metadata flow
- Add qualified name caching to optimization notes
- Update future work to reflect completed items
- Add new tests to "Tests to Add" checklist
- Document key runtime exports

https://claude.ai/code/session_01A35YqXXpYDgQaHzKgfkCEi
@sudo-owen sudo-owen merged commit 81036e2 into claude/solidity-typescript-transpiler-t9GlK Jan 26, 2026
1 check passed
@sudo-owen sudo-owen deleted the claude/optimize-solidity-typescript-compiler-PMAXu branch January 26, 2026 19:04
sudo-owen added a commit that referenced this pull request Jan 27, 2026
* Add Solidity to TypeScript transpiler

Initial implementation of sol2ts.py transpiler that converts Solidity
contracts to TypeScript for local simulation of the game engine.

Features:
- Full Solidity lexer/tokenizer with support for all operators and keywords
- Recursive descent parser for Solidity AST
- TypeScript code generation with BigInt support
- Handles structs, enums, contracts, interfaces, functions
- Supports tuple declarations and multi-dimensional arrays
- Basic Yul/inline assembly transpilation (marked as comments)
- Runtime library with storage simulation and bit manipulation helpers

Transpiled files:
- Engine.sol -> Engine.ts (60KB, main game engine)
- StandardAttack.sol, AttackCalculator.sol (move system)
- BasicEffect.sol (effect system)
- Enums.sol, Constants.sol, Structs.sol (data types)

* Improve Yul transpilation and simplify type casting

- Add proper Yul code normalization to handle tokenizer spacing
- Implement pattern matching for MonState clearing assembly pattern
- Simplify type casting to use BigInt() without verbose bit masking
- Reduce parentheses in binary operations for cleaner output
- Fix mstore, sload, sstore pattern matching

* Generalize transpiler with type registry and proper Yul parser

- Replace name-based heuristics with type registry for array/mapping detection
- Track variable types from declarations (state vars, locals, loop vars)
- Use is_array/is_mapping from TypeName AST for accurate index handling
- Rewrite Yul transpiler with AST-based approach instead of pattern matching
- Handle sload/sstore as generic storage operations (_storageRead/_storageWrite)
- Parse Yul let bindings, if statements, and function calls properly
- Track .slot references to map storage keys to variables

* Add type(T).max/min support and transpile struct/interface files

- Add _type_max and _type_min helpers to compute integer type bounds
- Handle type(int32).max, type(uint256).max, etc. in member access
- Transpile Structs.sol to generate TypeScript interfaces
- Transpile Enums.sol, Constants.sol with proper type handling
- Transpile interface files (IEngine, IValidator, IEffect, etc.)
- Re-transpile Engine.sol with updated transpiler

* Remove dead code and unused imports from transpiler

- Remove 161 lines of dead Yul code (old parse_yul_statements,
  transpile_yul_statement, transpile_yul_expression, parse_yul_args,
  transpile_yul_function, transpile_yul_function_expr methods) that
  were superseded by the new AST-based Yul transpiler
- Remove unused self.type_info and self.imports fields from
  TypeScriptCodeGenerator
- Remove unused os import

* Add transpiler output directory to .gitignore

Generated TypeScript files from the Solidity transpiler should not be
committed as they can be recreated by running the transpiler.

* Add test framework for transpiler validation

- Add vitest test framework with package.json and config
- Create engine.test.ts with tests for:
  - Battle key computation and consistency
  - Matchmaker authorization
  - MonState packing/unpacking
  - Storage operations
- Update transpiler to:
  - Import from runtime library (Contract, Storage)
  - Extend Contract base class for all classes
  - Add storage helper methods (_getStorageKey, _storageRead, _storageWrite)
  - Handle interface type casts (IMatchmaker(x) -> x)
  - Handle struct constructors (BattleData() -> {} as BattleData)
  - Fix error throwing syntax

* Remove ts-output from version control (now in .gitignore)

* Add node_modules to transpiler .gitignore

* Improve transpiler type handling and imports

- Add module imports from Structs, Enums, Constants for non-self files
- Track known structs, enums, constants, and interfaces for proper prefixing
- Add Structs./Enums./Constants. prefixes to type references
- Handle interfaces as 'any' type in TypeScript
- Add MappingAllocator methods (_initializeStorageKey, _getStorageKey, _freeStorageKey)
- Fix struct constructors to use proper module prefix
- Update Yul transpiler to prefix constant references
- Track current file type to avoid self-referencing prefixes

* Fix transpiler type handling: sha256, address literals, bigint indexing

- Add sha256 and sha256String functions to runtime using Node crypto
- Handle address and bytes32 type casts with proper hex padding
- Convert sha256(abi.encode("string")) to sha256String("string")
- Fix bigint index access by converting to Number() for arrays/mappings
- Add @types/node to tsconfig for Node.js type support
- Update imports to include Enums in Structs.ts

* Fix tuple destructuring, BigInt array size, and abi.decode handling

- Remove parens from tuple left-hand side in assignments
- Use empty string instead of '_' for ignored tuple components
- Fix BigInt 'n' suffix check to not strip from variable names like globalLen
- Add decodeAbiParameters to viem imports
- Handle abi.decode with proper argument swapping and type conversion

* Fix all TypeScript type errors in transpiled output

- Track function parameter types in var_types for proper type inference
- Convert enum values to Number() for viem's encodeAbiParameters (uint8)
- Cast address and bytes32 values to hex string type for viem
- Convert bytes32 casts of computed expressions to hex string format
- Handle small integer types (int32, uint8, etc.) with Number() conversion
- Add hex string type assertion for decodeAbiParameters data argument

* Refactor transpiler to properly handle contract inheritance

- Remove hard-coded MappingAllocator methods from generate_class
- Add storage helpers (_yulStorageKey, _storageRead, _storageWrite) to Contract base class
- Handle contract inheritance using base_contracts from parser
- Track known contract methods for this. prefix handling
- Add function overload handling for TypeScript (merge into optional params)
- Add delete statement parsing and code generation
- Fix mapping key type detection to avoid incorrect Number() conversion
- Import base contracts in generated TypeScript files

* Fix assembly transpilation to use type assertions for storage operations

When transpiling Yul .slot access and sload/sstore operations, cast storage
variables to 'any' type to handle struct references being used as storage keys.

* Fix constructor parsing for base constructor calls

Update parse_constructor to properly handle nested braces/parens in base
constructor calls (like ATTACK_PARAMS({...})) by tracking parenthesis depth
before looking for the constructor body brace.

* Add support for inherited members and static constants

- Track inherited state variables from base contracts (known_contract_vars)
- Use ClassName.CONST syntax for static/constant state variables
- Track current class name for static member access
- Add StandardAttack, Ownable, AttackCalculator to known contract methods/vars

* Replace vitest with simple tsx test runner

- Remove vitest dependency (saves 76 packages)
- Add tsx for running TypeScript tests directly
- Create simple test runner using Node's assert module
- Add scaffold battle test (2v2, speed determines turn order)
- Package-lock reduced from ~5000 to 808 lines

* Fix transpiler for library imports, tuple destructuring, and enum casting

- Add tracking for library contracts (AttackCalculator) and generate imports
- Fix tuple declaration parsing to preserve trailing comma elements
- Fix enum type casting to use TypeScript type assertions
- Add ATTACK_PARAMS to known structs
- Fix constructor parameter tracking to avoid incorrect this. prefix
- Add current_contract_kind tracking for library static methods
- Remove old vitest-based test file
- Fix tsconfig.json to remove vitest type reference

* Refactor transpiler: add get_qualified_name helper and auto-generate optional constructor params

Cleanup improvements:
- Add get_qualified_name() helper to consolidate Structs./Enums./Constants. prefix logic
- Remove unused known_events set (dead code)
- Use helper method in generate_identifier, YUL transpiler, function call generation
- Auto-generate optional constructor parameters for known base classes
- Wrap base class constructor body in conditional for optional params

This reduces code duplication and ensures consistent prefix handling across
different code generation paths.

* Fix transpiler correctness: base constructor args, struct literals, and static const prefixes

- Add BaseConstructorCall AST node and parser support to capture base
  constructor arguments (e.g., StandardAttack(...) in constructor header)
- Generate super() calls with actual arguments instead of empty super()
- Handle struct constructors with named arguments (e.g., ATTACK_PARAMS({...}))
  to generate full object literals instead of empty {} casts
- Fix identifier ordering to check class-local static constants before
  global constants, so BullRush.SELF_DAMAGE_PERCENT is used instead of
  Constants.SELF_DAMAGE_PERCENT
- Add heuristic for internal methods: function calls starting with _ get
  this. prefix when not already present, fixing inherited method calls

* Add comprehensive e2e tests for status effects, forced switches, and abilities

Tests cover:
- ZapStatus (paralysis): skip turn timing based on priority, auto-removal
- BurnStatus: damage over time (1/16 HP per round)
- Forced switches: user-initiated switches (HitAndDip pattern)
- Forced switches: opponent switches (PistolSquat pattern)
- UpOnly ability: attack boost on damage taken, stacking
- Complex scenarios: burn + ability interaction, multi-turn battles

Includes MockEngine with full effect lifecycle support:
- Effect application and removal
- Round start/end processing
- AfterDamage hooks for abilities
- Stat boost tracking

* Add CHANGELOG.md documenting transpiler features, future work, and known issues

* Fix multiple parser issues: using directives, unchecked blocks, array literals, and tuple patterns

Parser improvements:
- Add UNCHECKED, TRY, CATCH tokens and keyword handling
- Handle qualified library names in using directives (e.g. EnumerableSetLib.Uint256Set)
- Parse unchecked blocks as regular blocks
- Skip try/catch statements (return empty block)
- Add ArrayLiteral AST node for [val1, val2, ...] syntax
- Fix tuple declaration detection for leading commas (skipped elements)
- Handle qualified type names in variable declarations

Yul transpiler fixes:
- Add _split_yul_args helper for nested parentheses in function args
- Handle caller(), timestamp(), origin() built-in functions
- Add bounds checking for binary operation parsing

These changes enable successful transpilation of:
- GachaRegistry.sol
- BattleHistory.sol
- StatBoosts.sol
- BasicEffect.sol
- StatusEffect.sol and status effect implementations

* Update CHANGELOG with parser fixes and base class support

- Document all resolved parser issues (7 files now transpiling)
- Add Yul transpiler improvements (nested args, built-in functions)
- Document successful base class transpilation (BasicEffect, StatusEffect)
- Update version history with 2026-01-21 changes
- Remove completed items from future work section

* Add Solidity mapping semantics for TypeScript transpilation

Key changes:
- Generate initialization for nested mapping writes (mapping[a][b] = val)
- Add nullish coalescing for compound assignment on mappings (map[k] += val)
- Add default values for mapping reads in variable declarations
- Add mapping helper functions to runtime (mappingGet, mappingEnsure)

These changes enable correct Solidity-style mapping behavior in TypeScript:
- Nested mapping intermediate keys auto-initialize to empty objects
- Compound assignments (+=, -=) on mappings initialize to 0n first
- Reading from non-existent mapping keys returns default values (0n, false, etc.)

Also adds Engine.ts e2e test suite validating:
- Engine instantiation and method availability
- Deterministic battle key computation
- Matchmaker authorization
- Core engine methods (dealDamage, switchActiveMon, addEffect, etc.)

All 29 tests pass (5 unit + 14 e2e + 10 engine-e2e)

* Add mapping semantics, uint masking, and Engine e2e tests

- Auto-initialize nested mappings before writes (??= {})
- Auto-initialize before compound assignment on mappings (??= 0n)
- Add default values for mapping reads in variable declarations
- Fix bytes32/address defaults to proper zero hex strings
- Add bit masking for uint type casts < 256 bits
- Add comprehensive engine-e2e.ts test suite (17 tests)
- Create TestableEngine class for proper test initialization

* Update CHANGELOG with Engine integration progress and test coverage

- Mark Engine integration as partially complete
- Add runtime library mapping helpers documentation
- Add Engine E2E test coverage section

* Add EventStream system to replace console.log for events

- Add EventStream class for capturing contract events
- EventLog interface with name, args, timestamp, emitter, data
- Support filtering: getByName(), filter(), getLast(), has()
- Global globalEventStream shared by all contracts
- Custom streams via setEventStream()/getEventStream()
- Add 5 new tests for EventStream functionality (22 total)
- Update CHANGELOG with event stream documentation

* Update CHANGELOG: Engine integration now complete

- StatBoosts.ts transpiled for stat modification
- TypeCalculator.ts transpiled for type effectiveness
- All core engine contracts now transpiled

* Fix BigInt precision loss and add comprehensive battle simulation tests

- Use string format for large BigInt literals (>15 digits) to avoid JS precision loss
- Initialize uninitialized variables to Solidity default values (0n, false, etc.)
- Track public state variables to avoid incorrect getter function calls
- Fix Engine.sol otherPlayerIndex calculation using simpler expression
- Add comprehensive E2E battle simulation test that validates:
  - Battle initialization and setup
  - Mon switching
  - Damage calculation with type effectiveness
  - KO detection
  - Stamina consumption
  - Deterministic RNG from salts

* Add Angular client with battle service and move metadata extraction

- Create /client folder structure for Angular integration
- Add move metadata extraction script (extract-move-metadata.ts)
  that parses Solidity files for ATTACK_PARAMS and IMoveSet values
- Add metadata conversion functions to resolve constants and
  provide typed interfaces (MoveMetadata, BattleState, etc.)
- Implement Angular 20+ BattleService with:
  - Signal-based reactive state management
  - Local TypeScript battle simulation via transpiled Engine
  - Viem integration for on-chain interactions
  - Move metadata loading and querying
  - Salt generation and move commitment utilities
- Generate initial move-metadata.json with 44 moves across 11 mons

* Handle dynamic move properties gracefully and add UnboundedStrike tests

- Update metadata extractor to detect dynamic stamina (conditional returns)
- Add helper functions for function body extraction and dynamic logic detection
- Improve custom behavior detection to identify conditional-power, dynamic-stamina,
  and stack consumption patterns
- Update metadata-converter with hasDynamicStamina() and hasDynamicPower() helpers
- Fix transpiler to add super() call when extending Contract base class
- Add comprehensive UnboundedStrike battle simulation tests:
  * Stamina cost varies based on Baselight stacks (2 normal, 1 empowered)
  * Power scales with stacks (80 base, 130 empowered)
  * Empowered attack consumes all 3 Baselight stacks
  * Damage comparison test verifies empowered deals ~62% more damage
- Initialize effect storage with auto-vivifying proxy in test simulator

* Update CHANGELOG with dynamic move properties support

Document the generic approach to handling moves with runtime-calculated
values (dynamic power, stamina, priority). The system analyzes function
bodies for conditional logic rather than hardcoding specific move names.

* Clarify transpiler approach: correct transpilation over metadata

The transpiler should produce correct TypeScript that naturally behaves
like Solidity. Dynamic move properties work because conditional logic in
functions like stamina() and move() is correctly transpiled.

Removed references to metadata generation approach from changelog -
metadata/heuristics are unnecessary when the code transpiles correctly.

* Add non-standard move tests and document transpiler issues

Transpiled all 44 move contracts from src/mons:
- 23 IMoveSet implementations with custom logic
- 21 StandardAttack extensions (7 with custom move() logic)

Added tests for non-standard moves:
- DeepFreeze: conditional power based on Frostbite status
- RockPull: self-damage/priority based on opponent switch
- Gachachacha: RNG-based power (0-200)

Documented transpiler issues:
- abi.encode with string parameters incorrectly typed as uint256
- Missing dependency injection for StatBoosts

* Refactor: extract shared helpers and test utilities

- Extract _to_padded_address() and _to_padded_bytes32() helpers in sol2ts.py
  to eliminate duplicate hex conversion code
- Create shared test-utils.ts module with test framework functions
  (test, expect, runTests) used across all test files
- Update all test files to import from shared module, reducing duplication

* Fix abi.encode to correctly infer types from function return values

- Add method_return_types tracking to TypeRegistry
- Track return types for single-return functions during AST discovery
- Update TypeScriptCodeGenerator to look up return types for function calls
- Add _solidity_type_to_abi_type helper for consistent type conversion
- Add Python test suite for transpiler ABI encoding behavior

Before: abi.encode(id, name()) used uint256 for name() even when it returns string
After: abi.encode(id, name()) correctly uses string type based on function signature

This is a general fix that works for all types (string, address, uint*, etc.)
without special-casing any particular type.

* Generate imports for contract types used in state variables and params

- Add contracts_referenced tracking for contract types used as types
- Track contracts when generating type conversions in solidity_type_to_ts
- Generate imports for referenced contracts in generate_imports
- Add Python tests for contract type import generation

Before: Contract types like StatBoosts in state variables were not imported
After: Contract types are properly imported from their respective modules

This fixes the "missing dependency injection" issue where moves like
TripleThink and Deadlift use StatBoosts as a constructor parameter
but the import was not generated.

* Move transpiler from scripts/ to root and add contract address support

- Move scripts/transpiler/ to transpiler/ at repository root
- Update .gitignore and client imports to reflect new location
- Add ContractAddressRegistry for configuring contract addresses
- Add _contractAddress property to Contract base class
- Update transpiler to use _contractAddress for address(this)
- Handle IEffect(address(this)) pattern to return object reference
- Handle uint160/uint192(address(this)) with addressToUint helper
- All tests passing

* Rewrite CHANGELOG.md as comprehensive transpiler documentation

Replace changelog format with proper documentation covering:
- Architecture overview with ASCII diagrams
- Transpilation pipeline phases (lexer, parser, codegen)
- Step-by-step guide for adding new Solidity files
- Angular integration patterns and BattleService setup
- Contract address system configuration options
- Supported features checklist
- Known limitations and workarounds
- Future work priorities
- Test coverage summary and tests to add
- Quick reference for CLI usage and file structure

* Migrate move metadata extraction to transpiler with DI container (#49)

* Add transpiler optimizations, metadata emission, and dependency injection

Optimizations:
- Add qualified name cache to TypeRegistry for O(1) lookups
- Pre-build qualified name lookup dict per file during generation
- Reduces repeated set membership checks in get_qualified_name()

Metadata Emission (--emit-metadata, --metadata-only flags):
- Add ContractMetadata and ContractDependency dataclasses
- MetadataExtractor class extracts contract info from AST:
  - Dependencies from constructor parameters
  - Constants and move properties
  - Public methods and inheritance info
- DependencyManifest tracks all contracts and generates:
  - dependency-manifest.json with full contract metadata
  - factories.ts with factory functions for DI

Dependency Injection Container (runtime):
- Add ContractContainer class with:
  - registerSingleton() for shared instances
  - registerFactory() for per-resolve instances
  - registerLazySingleton() for deferred singletons
  - resolve() with automatic dependency resolution
  - Circular dependency detection
- Add globalContainer instance
- Enables automatic wiring of contract dependencies

This replaces the brittle move-metadata.json extraction with
transpiler-integrated metadata emission, ensuring consistency
between generated code and metadata.

https://claude.ai/code/session_01A35YqXXpYDgQaHzKgfkCEi

* Remove old move-metadata extraction in favor of transpiler metadata

- Delete client/scripts/extract-move-metadata.ts (481 lines)
- Delete client/generated/move-metadata.json
- Remove extract-metadata npm script from client/package.json
- Update comment in metadata-converter.ts

The transpiler now handles metadata extraction via --emit-metadata flag,
which generates dependency-manifest.json with equivalent information
plus dependency graphs for automatic contract wiring.

https://claude.ai/code/session_01A35YqXXpYDgQaHzKgfkCEi

* Update CHANGELOG with metadata emission, DI container, and Angular integration

Major documentation updates:
- Add new "Metadata and Dependency Injection" section
- Document --emit-metadata and --metadata-only CLI flags
- Document ContractContainer API and usage patterns
- Update Angular integration to use DI container
- Update architecture diagram to show metadata flow
- Add qualified name caching to optimization notes
- Update future work to reflect completed items
- Add new tests to "Tests to Add" checklist
- Document key runtime exports

https://claude.ai/code/session_01A35YqXXpYDgQaHzKgfkCEi

---------

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants