fix: Option<T> generates Optional<T> fields instead of PlutusData (#573)#573
Open
matiwinnetou wants to merge 1 commit intomasterfrom
Open
fix: Option<T> generates Optional<T> fields instead of PlutusData (#573)#573matiwinnetou wants to merge 1 commit intomasterfrom
matiwinnetou wants to merge 1 commit intomasterfrom
Conversation
65cacb5 to
10d451c
Compare
93e31c5 to
9db45a6
Compare
Contributor
Author
|
Q: Why is @plutus/src/main/java/com/bloxbean/cardano/client/plutus/blueprint/PlutusBlueprintLoader.java handling only Option$ and Option< type, what about other container types like: List<, Pair<, etc etc A: |
e1f0b42 to
1e9cf24
Compare
matiwinnetou
pushed a commit
that referenced
this pull request
Feb 12, 2026
**Problem:**
Blueprint fields referencing Option<T> definitions generated untyped PlutusData
instead of type-safe Optional<T>, requiring manual casting and losing compile-time
type safety. This affected both Aiken v1.0.26 (Option$T) and v1.1.21+ (Option<T>)
syntax, causing SundaeSwap V2/V3 blueprints to have opaque PlutusData fields where
typed Optional<T> was expected.
**Root Cause:**
PlutusBlueprintLoader only detected Option$ prefix (line 119), missing Option<
syntax from newer Aiken versions. When Option<T> definitions weren't marked with
dataType=option, they bypassed OptionDataTypeProcessor and fell back to PlutusData.
**Solution:**
1. **PlutusBlueprintLoader.java** - Extended Option detection to both syntaxes:
- Added definitions resolution loop (lines 59-70) to set dataType=option
- Extended check from `ref.startsWith("Option$")` to include `"Option<"`
- Added null check for blueprints without definitions
- Now routes all Option types through OptionDataTypeProcessor → Optional<T>
2. **BlueprintAnnotationProcessor.java** - Smart generic type handling:
- Added resolveDefinitionKeyForClassGeneration() to distinguish built-in
containers (List, Option, Tuple) from domain types (Interval, ValidityRange)
- Built-in containers return null (skip generation), domain types return base
type for typed class generation
- Supports both $ and <> syntax across Aiken v1.0.x and v1.1.x
- Changed methods to package-private for testing (no reflection needed)
3. **FieldSpecProcessor.java** - Removed buggy workaround:
- Reverted resolveBuiltInContainerTypeName() added by previous attempt
- Simplified PlutusData fallback for truly unresolvable types
- Now relies on PlutusBlueprintLoader fix for proper type routing
**Test Coverage:**
1. **PlutusBlueprintLoaderTest.java** - New OptionTypeResolution nested class:
- Tests Option$ and Option< syntax both set dataType=option
- Verifies Some/None anyOf variants preserved after resolution
- Tests null definitions handling (no NPE)
- Validates SundaeSwap V2 and V3 blueprint loading
2. **BlueprintAnnotationProcessorTest.java** - Consolidated comprehensive tests:
- 56 tests organized in @nested classes (no reflection, uses package-private)
- IsBuiltInGenericContainerTests (9 built-ins, 7 domain types, 3 edge cases)
- ResolveDefinitionKeyForClassGenerationTests (30+ tests covering $ and <>)
- GenericTypeSkipTests (integration tests for compilation success)
- Deleted old reflection-based test files
3. **FieldSpecProcessorTest.java** - Fixed misleading comments:
- Clarified resolveClassNameFromRef() tests class generation, not field typing
- Corrected "PlutusData fallback" comments to explain Option<T> → Optional<T>
- Added notes distinguishing skip generation from field type resolution
4. **SundaeSwapV2Test.java & SundaeSwapV3Test.java** - PlutusData assertions:
- Added verifyNoOpaqueTypes() to detect illegitimate PlutusData fields
- Filters out legitimate uses (extension/data fields per CIP-57)
- Ensures Option<T> generates Optional<T>, not PlutusData
**Impact:**
Before (❌):
```java
private PlutusData stakeCredential; // Option<Credential> → untyped
private PlutusData validityRange; // Interval$Int → untyped
```
After (✅):
```java
private Optional<StakeCredential> stakeCredential; // Option<Credential> → typed
private Interval validityRange; // Interval$Int → typed
```
**Real-World Validation:**
- SundaeSwap V2 (Aiken v1.0.26): Interval$Int generates typed Interval class
- SundaeSwap V3 (Aiken v1.1.21): Option<Credential> generates Optional<Credential>
- All tests pass: ./gradlew :annotation-processor:test :plutus:test
**Breaking Changes:** None - existing blueprints gain better type safety
1e9cf24 to
32f02a5
Compare
**Problem:**
Blueprint fields referencing Option<T> definitions generated untyped PlutusData
instead of type-safe Optional<T>, requiring manual casting and losing compile-time
type safety. This affected both Aiken v1.0.26 (Option$T) and v1.1.21+ (Option<T>)
syntax, causing SundaeSwap V2/V3 blueprints to have opaque PlutusData fields where
typed Optional<T> was expected.
**Root Cause:**
PlutusBlueprintLoader only detected Option$ prefix (line 119), missing Option<
syntax from newer Aiken versions. When Option<T> definitions weren't marked with
dataType=option, they bypassed OptionDataTypeProcessor and fell back to PlutusData.
**Solution:**
1. **PlutusBlueprintLoader.java** - Extended Option detection to both syntaxes:
- Added definitions resolution loop (lines 59-70) to set dataType=option
- Extended check from `ref.startsWith("Option$")` to include `"Option<"`
- Added null check for blueprints without definitions
- Now routes all Option types through OptionDataTypeProcessor → Optional<T>
2. **BlueprintAnnotationProcessor.java** - Smart generic type handling:
- Added resolveDefinitionKeyForClassGeneration() to distinguish built-in
containers (List, Option, Tuple) from domain types (Interval, ValidityRange)
- Built-in containers return null (skip generation), domain types return base
type for typed class generation
- Supports both $ and <> syntax across Aiken v1.0.x and v1.1.x
- Changed methods to package-private for testing (no reflection needed)
3. **FieldSpecProcessor.java** - Removed buggy workaround:
- Reverted resolveBuiltInContainerTypeName() added by previous attempt
- Simplified PlutusData fallback for truly unresolvable types
- Now relies on PlutusBlueprintLoader fix for proper type routing
**Test Coverage:**
1. **PlutusBlueprintLoaderTest.java** - New OptionTypeResolution nested class:
- Tests Option$ and Option< syntax both set dataType=option
- Verifies Some/None anyOf variants preserved after resolution
- Tests null definitions handling (no NPE)
- Validates SundaeSwap V2 and V3 blueprint loading
2. **BlueprintAnnotationProcessorTest.java** - Consolidated comprehensive tests:
- 56 tests organized in @nested classes (no reflection, uses package-private)
- IsBuiltInGenericContainerTests (9 built-ins, 7 domain types, 3 edge cases)
- ResolveDefinitionKeyForClassGenerationTests (30+ tests covering $ and <>)
- GenericTypeSkipTests (integration tests for compilation success)
- Deleted old reflection-based test files
3. **FieldSpecProcessorTest.java** - Fixed misleading comments:
- Clarified resolveClassNameFromRef() tests class generation, not field typing
- Corrected "PlutusData fallback" comments to explain Option<T> → Optional<T>
- Added notes distinguishing skip generation from field type resolution
4. **SundaeSwapV2Test.java & SundaeSwapV3Test.java** - PlutusData assertions:
- Added verifyNoOpaqueTypes() to detect illegitimate PlutusData fields
- Filters out legitimate uses (extension/data fields per CIP-57)
- Ensures Option<T> generates Optional<T>, not PlutusData
**Impact:**
Before (❌):
```java
private PlutusData stakeCredential; // Option<Credential> → untyped
private PlutusData validityRange; // Interval$Int → untyped
```
After (✅):
```java
private Optional<StakeCredential> stakeCredential; // Option<Credential> → typed
private Interval validityRange; // Interval$Int → typed
```
**Real-World Validation:**
- SundaeSwap V2 (Aiken v1.0.26): Interval$Int generates typed Interval class
- SundaeSwap V3 (Aiken v1.1.21): Option<Credential> generates Optional<Credential>
- All tests pass: ./gradlew :annotation-processor:test :plutus:test
**Breaking Changes:** None - existing blueprints gain better type safety
32f02a5 to
9bd405b
Compare
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Problem:
Blueprint fields referencing Option definitions generated untyped PlutusData
instead of type-safe Optional, requiring manual casting and losing compile-time
type safety. This affected both Aiken v1.0.26 (Option$T) and v1.1.21+ (Option)
syntax, causing SundaeSwap V2/V3 blueprints to have opaque PlutusData fields where
typed Optional was expected.
Root Cause:
PlutusBlueprintLoader only detected Option$ prefix (line 119), missing Option<
syntax from newer Aiken versions. When Option definitions weren't marked with
dataType=option, they bypassed OptionDataTypeProcessor and fell back to PlutusData.
Solution:
PlutusBlueprintLoader.java - Extended Option detection to both syntaxes:
ref.startsWith("Option$")to include"Option<"BlueprintAnnotationProcessor.java - Smart generic type handling:
containers (List, Option, Tuple) from domain types (Interval, ValidityRange)
type for typed class generation
FieldSpecProcessor.java - Removed buggy workaround:
Test Coverage:
PlutusBlueprintLoaderTest.java - New OptionTypeResolution nested class:
BlueprintAnnotationProcessorTest.java - Consolidated comprehensive tests:
FieldSpecProcessorTest.java - Fixed misleading comments:
SundaeSwapV2Test.java & SundaeSwapV3Test.java - PlutusData assertions:
Impact:
Before (❌):
After (✅):
Real-World Validation:
Breaking Changes: None - existing blueprints gain better type safety