Skip to content

Auto-generate XDR types from canonical .x definitions#72

Merged
christian-rogobete merged 51 commits intomainfrom
xdr-gen
Mar 11, 2026
Merged

Auto-generate XDR types from canonical .x definitions#72
christian-rogobete merged 51 commits intomainfrom
xdr-gen

Conversation

@christian-rogobete
Copy link
Member

Summary

Replaces ~309 hand-written XDR type definitions with machine-generated PHP code produced by a Ruby-based code generator that reads Stellar's canonical .x XDR definition files. Adds 133 new XDR types not previously in the SDK (SCP, overlay, bucket, ledger close meta, exporter, survey, internal types), extracts hand-maintained helper methods into 34 wrapper classes, and adds 1,013 auto-generated round-trip unit tests covering all generated types.

  • Code generator at tools/xdr-generator/ (Ruby, uses xdrgen gem)
  • 13 .x definition files in xdr/ from stellar/stellar-xdr
  • Generated PHP files in Soneso/StellarSDK/Xdr/

What changed

  • Machine-generated XDR types: 434 generated PHP files, each with encode(), decode(), toBase64Xdr(), and fromBase64Xdr() methods. All types are produced from the canonical .x definitions via the xdrgen gem.
  • Code generator: tools/xdr-generator/generator/generator.rb reads .x files via the xdrgen gem and produces PHP. Override files (name_overrides.rb, field_overrides.rb, member_overrides.rb, type_overrides.rb) preserve existing SDK naming where the hand-written code diverged from XDR spec names.
  • 34 base+wrapper pairs: Types that need hand-maintained helper methods (factory methods, custom constructors, StrKey encoding, etc.) generate a *Base.php file; the hand-maintained wrapper extends the generated base class. This separates generated encode/decode logic from SDK-specific convenience methods.
  • Generator infrastructure: Makefile with 9 Docker-based targets, GitHub Actions CI workflow (xdr-generator.yml) with snapshot tests, drift detection (fails if generated files differ from committed versions), and validation against .x definitions.
  • Auto-generated round-trip tests: 1,013 tests in Soneso/StellarSDKTests/Unit/Xdr/Generated/ covering enums, structs, unions, and typedefs — each test includes both binary encode/decode and base64 round-trip verification.
  • Enum decode validation: All generated enum decode() methods now reject unknown discriminant values with InvalidArgumentException instead of silently accepting them.

Breaking changes

This PR has breaking changes. The high-level SDK APIs (operation classes, services, SEP implementations) are unchanged, with the exception listed below. The remaining breaks are in the low-level XDR layer.

High-level API

SorobanContractInfo.envInterfaceVersion replaced by two properties

SorobanContractInfo.envInterfaceVersion: int is replaced by two properties: envMetaProtocol: int and envMetaPreRelease: int. This follows a change in the XDR spec where XdrSCEnvMetaEntryInterfaceVersion is now a struct with two uint32 fields (protocol, preRelease) instead of a single uint64.

// Before
$version = $contractInfo->envInterfaceVersion;

// After
$protocol = $contractInfo->envMetaProtocol;
$preRelease = $contractInfo->envMetaPreRelease;

Low-level XDR types

Most SDK consumers don't interact with XDR types directly — the SDK's operation classes, transaction builder, and service layer handle serialization internally. If your code does use XDR types, here is what changed.

XdrChangeTrustAsset / XdrTrustlineAsset no longer extend XdrAsset

Per the XDR spec, ChangeTrustAsset and TrustLineAsset are independent unions — they are not subtypes of Asset. The hand-written SDK had XdrChangeTrustAsset extends XdrAsset and XdrTrustlineAsset extends XdrAsset, which is incorrect.

Code that passes XdrChangeTrustAsset or XdrTrustlineAsset to Asset::fromXdr() must use the new factory methods instead:

// Before
Asset::fromXdr($changeTrustOp->getLine());  // worked because XdrChangeTrustAsset extended XdrAsset

// After
Asset::fromXdrChangeTrustAsset($changeTrustOp->getLine());  // new factory method
Asset::fromXdrTrustlineAsset($trustlineAsset);               // new factory method

AssetTypePoolShare::toXdr() now throws \RuntimeException. Use AssetTypePoolShare::toXdrChangeTrustAsset() instead.

Union constructor changes

Generated union types now take only the discriminant in their constructor. Associated values are set via public properties after construction:

// Before (some hand-written unions accepted arm values in constructor)
$result = new XdrManageOfferResult($code, $success);

// After
$result = new XdrManageOfferResult($code);
$result->success = $successValue;

Bugs fixed

The generator found and automatically fixed bugs across that were present in the previous, hand-written XDR type definitions.

Missing enum constants
  • XdrLiquidityPoolDepositResultCode — missing 3 constants (LINE_FULL, BAD_PRICE, POOL_FULL)
  • XdrOperationResultCode — missing constant TOO_MANY_SPONSORING
  • XdrSetOptionsResultCode — missing constant AUTH_REVOCABLE_REQUIRED
  • XdrContractCostType — missing 25 BLS12-381 constants
Incorrect encode output
  • XdrInvokeHostFunctionResult — double concatenation ($bytes .= $bytes .= ...) doubled the encoded output for SUCCESS arms
  • XdrSCError — 9 union arms incorrectly treated as void, silently dropping the SCErrorCode value
  • XdrSorobanTransactionMeta — copy-paste bug: diagnosticEvents array encoded $this->events instead of $this->diagnosticEvents
  • XdrInnerTransactionResultResult — encode() always wrote operations array regardless of result code
Other
  • XdrInnerTransactionResultPair$$transactionHashBytes variable-variable typo; fromBase64Xdr() returned wrong type

New infrastructure

Component Description
Makefile 9 targets: xdr-generate, xdr-clean-generated, xdr-clean-all, xdr-update, xdr-compare, xdr-generator-test, xdr-generator-update-snapshots, xdr-validate, xdr-generate-tests
.github/workflows/xdr-generator.yml CI: snapshot tests + drift detection (fails if generated files are stale) + validation
.gitattributes Marks generated files as linguist-generated=true, excludes wrappers and infrastructure
tools/xdr-generator/test/generator_snapshot_test.rb 11 Minitest tests, 8 golden snapshot files covering all generator patterns
tools/xdr-generator/test/generate_tests.rb Generates 1,013 round-trip encode/decode + base64 unit tests for all XDR types
tools/xdr-generator/test/validate_generated_types.rb Validates all 434 generated types against .x definitions (field names, enum constants, union arms)

Test plan

  • ./vendor/bin/phpunit --testsuite unit — 3,374 tests pass (including 1,013 auto-generated XDR round-trip tests)
  • make xdr-generator-test — 11/11 snapshot tests pass
  • make xdr-validate — 434 types validated, 0 errors
  • make xdr-update — clean regeneration matches committed files
  • All integration tests pass

christian-rogobete and others added 30 commits March 3, 2026 14:36
Ruby-based XDR code generator that reads .x definition files via the
xdrgen gem AST and produces PHP classes matching existing SDK patterns.
Includes Docker-based build (Makefile), override system for name/field/
type mappings, SKIP_TYPES protection for 309 existing classes, snapshot
test infrastructure, and .x XDR source files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract encode/decode into *Base.php files for types with custom SDK
methods. Wrappers now extend their Base class and retain only factory
methods, conversions, and helpers. Base decode() uses `new static()`
for proper late static binding. Updated generator to emit `static`
return types for BASE_WRAPPER_TYPES.

Types: XdrSCVal, XdrHostFunction, XdrLedgerKey, XdrTransaction,
XdrMuxedAccount, XdrAccountID, XdrSCAddress, XdrClaimableBalanceID,
XdrMuxedAccountMed25519, XdrContractExecutable, XdrContractIDPreimage,
XdrDecoratedSignature

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hand-written enums with generated versions: XdrAssetType,
XdrMemoType, XdrClaimantType, XdrClaimPredicateType, XdrLiquidityPoolType,
XdrRevokeSponsorshipType, XdrPreconditionType.

Adds member overrides for 4 enums that strip XDR type prefixes from
constant names. Fixes XdrMemoType decode() bug (was returning
XdrEnvelopeType). Adds toBase64Xdr/fromBase64Xdr to all generated enums.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add MEMBER_PREFIX_STRIP feature to generator for scalable constant name
mapping, regenerate 29 enum types (26 result codes + 3 simple type enums),
and fix enum $value visibility to public for backward compatibility.
Adds 5 missing constants from XDR spec across 3 types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…er support (Batch 3)

Add typedef chain resolution infrastructure to follow chains like
PoolID → Hash → opaque[32] for correct encode/decode calls. Populate
TYPE_OVERRIDES (17 entries), FIELD_OVERRIDES (5 types), and
FIELD_TYPE_OVERRIDES (9 types for BigInteger fields). Generate
getters/setters for backward compatibility.

Fixes 3 bugs in hand-written code:
- XdrClaimOfferAtom: signed/unsigned mismatch for offerID
- XdrCreatePassiveSellOfferOperation: spurious $xdr argument in decode
- XdrSimplePaymentResult: wrong destination type (MuxedAccount → AccountID)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… enums (Batch 4)

Add factory method generation for all enum types. Each constant gets a
static factory (e.g., XdrOperationType::PAYMENT()). Add FACTORY_PREFIX_STRIP
for 5 types where factory names strip a prefix from constant names,
FACTORY_NAME_OVERRIDES for XdrSCValType's inconsistent naming, and
FACTORY_ALIASES for XdrLedgerEntryType::EXPIRATION() backward compat.

Previously-generated enums (Batch 1 & 2) also receive factory methods
as a purely additive change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…and SCSymbol typedef (Batch 5)

Regenerate 29 struct types including envelope types with array fields,
Soroban types, ledger key types, and SCSpec types. Add XdrSCSymbol to
TYPE_OVERRIDES for string typedef resolution. Add FIELD_OVERRIDES for
reserved-word property name preservation (function field). Add BigInteger
overrides for XdrLiabilities.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…pedef array support (Batch 6)

Regenerate 28 structs and 2 enums, including SCSpec types (struct fields,
enum cases, events, functions, unions), operation types (AllowTrust,
SetTrustLineFlags, ClaimClaimableBalance, Clawback), entry types, and
meta types. Add field name overrides for authorize→authorized,
trustor→accountID, val→value. Fix signed→unsigned encoding for
AccountEntryV3.seqTime and AllowTrustOperation.authorized.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, and event types (Batch 7)

Regenerate XdrBumpSequenceOperation, XdrClawbackOperation, XdrContractDataEntry,
XdrPathPaymentStrictReceiveOperation, XdrPathPaymentStrictSendOperation,
XdrTransactionEvent, and XdrTransactionEventStage. Adds FIELD_TYPE_OVERRIDES
for BigInteger amount fields. Fixes latent silent-corruption bug in PathPayment
encode() where instanceof guard could produce mismatched array length.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… and factories (Batch 8)

Regenerate XdrContractCostType (fix instance decode, add 25 missing BLS12-381
constants), XdrTrustLineFlags (fix wrong decode return type), XdrInvokeHostFunctionResultCode,
XdrSCEnvMetaKind, XdrSCMetaKind, XdrSCSpecEntryKind, XdrSCSpecUDTUnionCaseV0Kind,
XdrSorobanAuthorizedFunctionType, and XdrSorobanCredentialsType. Adds factory
prefix strips and custom factory name overrides.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…uard fixes (Batch 9)

Regenerate XdrOperationMeta, XdrTransactionMetaV1, XdrTransactionMetaV2, and
XdrCreateClaimableBalanceOperation. Add typedef-array inlining to the generator:
when TYPE_OVERRIDES maps a typedef to "array", struct fields using that typedef
are inlined as PHP arrays with encode/decode loops instead of creating a wrapper
class. Fixes instanceof guard bugs in all 4 hand-written files that could produce
corrupt XDR. Side-effect fix to XdrOperationMetaV2 for the same typedef-array
pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… alias (Batch 10)

First union generation: regenerate 6 void-only result unions (XdrClawbackResult,
XdrClawbackClaimableBalanceResult, XdrRevokeSponsorshipResult, XdrExtendFootprintTTLResult,
XdrRestoreFootprintResult, XdrManageDataResult) and 1 struct (XdrEvictionIterator).

Generator infrastructure: add union accessor generation (getters/setters for
discriminant and arm fields), union discriminant field name overrides via
FIELD_OVERRIDES, boolean "is" getter alias for struct fields (isFoo() instead
of getIsFoo()), and fix format_case_label to resolve enum member names correctly
in union switch cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…attern (Batch 11)

Regenerate 12 void-only result union types (AllowTrust, BeginSponsoring,
BumpSequence, ChangeTrust, ClaimClaimableBalance, CreateAccount,
EndSponsoring, LiquidityPoolDeposit, LiquidityPoolWithdraw, Payment,
SetOptions, SetTrustLineFlags) and re-generate 6 Batch 10 types to pick
up the optional constructor change.

Infrastructure: make union constructor optional (?Type $disc = null) to
support no-arg construction pattern used by existing SDK code.

Fix XdrCreateAccountResult bug: hand-written code used wrong discriminant
type (XdrOperationResultCode instead of XdrCreateAccountResultCode).
Tests updated to match correct type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rms (Batch 12)

Generate first non-void union arm types: XdrExtensionPoint (void-only
int-disc), XdrAccountMergeResult (BigInteger arm), XdrInvokeHostFunctionResult
(Hash/opaque[32] arm), XdrInflationResult (InflationPayout array arm).

Infrastructure: apply FIELD_TYPE_OVERRIDES to union arms for BigInteger
support, add BigInteger import handling for unions, fix nullable type hint
for array arms in union accessors.

Fix XdrInvokeHostFunctionResult double-concat encoding bug ($bytes .=
$bytes .=), XdrInflationResult instanceof guard, and XdrAccountMergeResult
nullability-based encoding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… signed/unsigned bugs (Batch 13)

Generate XdrManageBuyOfferOperation, XdrManageSellOfferOperation,
XdrLiquidityPoolConstantProductParameters, XdrFeeBumpTransactionExt,
and XdrTransactionV0Ext. Fixes spurious $xdr argument in
readBigInteger64() and signed/unsigned mismatch for offerID in both
offer operations. Updates AssetTypePoolShare to pass fee explicitly
after removing LIQUIDITY_POOL_FEE_V18 constant default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ypedef (Batch 14)

Generate XdrSequenceNumber (BigInteger typedef),
XdrDataEntryExt, XdrOfferEntryExt, XdrLedgerEntryV1Ext (void-only
int-disc unions), XdrTransactionExt and XdrSorobanTransactionDataExt
(non-void int-disc unions with case 1 data arms). Migrates all
XdrSequenceNumber::getValue() callers to public property access and
updates 2-arg union constructors to 2-step property assignment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…g (Batch 15)

Replace 9 hand-written int-discriminated ext unions with generated versions.
Fixes XdrClaimableBalanceEntryExt encoding that used nullability check instead
of proper discriminant switch. Updates 7 test files to use 1-arg constructor
pattern with property assignment for non-void arms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nd base/wrapper pattern (Batch 16)

Fix double-?? generator bug where php_type_string() added redundant ?
prefix for optional types. Add EXTENSION_POINT_FIELDS support for
XdrClaimableBalanceEntryExtV1 and XdrTrustLineEntryExtensionV2. Use
FIELD_TYPE_OVERRIDES for XdrDataEntry (XdrDataValueMandatory). Introduce
XdrLedgerKeyAccount as first struct BASE_WRAPPER_TYPE with forAccountId()
helper preserved in wrapper.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n, and field renames (Batch 17)

Generate XdrLedgerEntry, XdrLiquidityPoolEntry, XdrContractCodeEntry,
XdrContractEvent, XdrSignedPayload, and XdrSorobanTransactionMetaV2.
Add XdrContractID to TYPE_OVERRIDES as string (Hash-based typedef).
Use FIELD_OVERRIDES for hash->cHash and contractID->hash renames.
Use FIELD_TYPE_OVERRIDES for XdrDataValueMandatory on ContractCodeEntry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Batch 18)

Generate 7 direct union replacements and 2 base/wrapper pairs. Fix
XdrSCError where hand-written code treated SCE_WASM_VM through SCE_AUTH
as void arms — XDR spec says they all carry SCErrorCode code. Add field
overrides for XdrSCMetaEntry/XdrSCEnvMetaEntry discriminant renames.
Update 3 test files to match generated API changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e fix (Batch 19)

Replace 3 union types (XdrHashIDPreimage, XdrLedgerEntryChange,
XdrRevokeSponsorshipOperation) and generate 3 base/wrapper pairs
(XdrMemo, XdrSorobanAuthorizedFunction, XdrSorobanTransactionData).
Fixes XdrRevokeSponsorshipOperation null-check encode bug and adds
missing XdrLedgerEntryChange restored getter/setter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…bug (Batch 20)

Replace 4 hand-written XDR types with generated versions. Fixes
critical copy-paste bug in XdrSorobanTransactionMeta where encode()
iterated $this->events twice instead of $this->diagnosticEvents,
producing corrupt XDR. Also fixes instanceof guard and offerID
signed/unsigned mismatch in XdrClaimOfferAtomV0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…guard fixes (Batch 21)

Regenerate XdrPreconditions (union), XdrTransactionMeta (int-disc union),
XdrTransactionMetaV3 and XdrTransactionMetaV4 (structs). Fixes instanceof
guards in encode() that silently skipped non-matching array entries
(count/items mismatch risk). Adds switch/case to Preconditions, public
field visibility, and toBase64Xdr/fromBase64Xdr.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…of fixes (Batch 22)

Regenerate XdrClaimPredicate (recursive union) and XdrTrustLineEntry (struct).
Fixes instanceof guards in ClaimPredicate AND/OR encode, corrects NOT arm from
array-of-1 pattern to proper XDR optional (present flag). Removes
XdrAccountMergeOperation from SKIP_TYPES (not an XDR-defined type, unchanged).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ides (Batch 23)

XdrLedgerEntryData: 10-arm union, field override ttl→ttlEntry, consistent camelCase.
XdrContractCostParams: typedef array, field override contractCostParams→entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…iscriminant access (Batch 24)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…amed inner struct (Batch 25)

XdrContractCodeEntryExt: 2-arm union (void/v1), inner struct renamed from
XdrContractCodeEntryExtV1 to XdrContractCodeEntryV1 per generator naming.
Old XdrContractCodeEntryExtV1.php is now orphaned (no external callers).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…26-28)

Batch 26: XdrSetOptionsOperation (all-optional struct), XdrPathPaymentResultSuccess
Batch 27: XdrPreconditionsV2 (Base/wrapper pattern for no-arg constructor compat)
Batch 28: XdrPathPaymentStrictReceiveResult, XdrPathPaymentStrictSendResult

Generator improvements:
- Reorder constructor params: required first, optional last (PHP 8.1+ compliance)
- Decode args reordered to match constructor signature

Bug fixes in hand-written code:
- PathPayment results: switch/case replaces if/else, adds missing break statements
- XdrOperationResultTest: removed 4 dead-code lines masking uninitialized property bug
- Test callsites updated for new constructor param order (XdrLedgerTest, XdrTransactionMetaTest)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… 29-30)

Batch 29: Verify XdrContractCodeEntryExtV1, XdrContractEventBodyV0
(generated identically, removed from SKIP_TYPES).

Batch 30: Regenerate XdrSCContractInstance (optional array struct),
XdrLiquidityPoolBody (union), XdrInnerTransactionResultResult (union
with encode bug fix — hand-written was missing switch in encode).

Generator fix: Array encode/decode path now handles is_optional for
TypedefArray* fields (e.g. SCMap* storage), adding the required
presence flag.

Config: Add XdrSCMap => "array" type override, fix
LiquidityPoolEntryConstantProduct name override key.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s (Batch 31)

Regenerate XdrTransactionResultResult with discriminant field override
(code → resultCode) preserving all caller compatibility. Generated
encode now has explicit switch cases for all 17 discriminant values
including 15 void error arms.

Add field name overrides for XdrOperationResult and XdrSCSpecEntry
(for future use when their other blockers are resolved).

Clean up SKIP_TYPES: remove 4 permanent non-XDR entries (XdrBuffer,
XdrDecoder, XdrEncoder, XdrDataValueMandatory).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
christian-rogobete and others added 19 commits March 4, 2026 23:12
…h 32)

Add name override ContractEventV0 → XdrContractEventBodyV0 to fix
inner struct naming mismatch. Regenerate XdrContractEventBody union
with proper default case and getter/setter/base64 methods.

Remove XdrContractEventBody and XdrContractEventV0 from SKIP_TYPES.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nerate XdrContractEventBodyV0 (Batches 33-34)

XdrSCSpecEntry: generated base handles encode/decode, wrapper preserves 6 factory methods.
XdrSCSpecTypeDef: generated base handles encode/decode with 19 void + 7 arm cases, wrapper preserves 25 factory methods.
XdrContractEventBodyV0: regenerated — removes instanceof guard, adds base64 helpers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…apper

- XdrSignerKeyType: generated base has full XDR constant names, wrapper
  preserves short-name aliases (ED25519, PRE_AUTH_TX, etc.) for backward
  compatibility. Adds BASE_WRAPPER support to enum generator.
- XdrSignerKey: regenerated with proper switch-based encode (replaces
  buggy truthiness-based if/else chain that would mishandle null bytes).
  Field override preserves signedPayload name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d XdrFeeBumpTransaction to base wrapper pattern

- XdrAllowTrustOperationAsset: wrapper overrides encode/decode to preserve
  asset code padding and null-stripping, keeps fromAlphaNumAssetCode() factory
- XdrTransactionEnvelope: wrapper preserves fromEnvelopeBase64XdrString() factory
- XdrFeeBumpTransaction: wrapper preserves optional $ext constructor default

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… support

- XdrConstantProduct, XdrClaimableBalanceEntry: direct regeneration with
  BigInteger field type overrides
- XdrAssetAlphaNum4, XdrAssetAlphaNum12: base wrapper with asset code
  padding and null-stripping overrides
- XdrChangeTrustOperation: base wrapper preserving MAX_LIMIT constant
  and optional limit default
- XdrConfigUpgradeSetKey: base wrapper with hex encode/decode overrides
- XdrLiquidityPoolDepositOperation, XdrLiquidityPoolWithdrawOperation:
  base wrapper with BigInteger fields and hex pool ID conversion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… and XdrManageOfferResult to base wrapper

- XdrOfferEntry: direct regeneration, fix incorrect BigInteger override
  (was on offerID, should be on amount), fix signed/unsigned mismatch
- XdrInnerTransactionResultPair: base wrapper with hex hash encoding,
  fixes double-dollar-sign bug and wrong fromBase64Xdr return type
- XdrManageOfferResult: base wrapper preserving 2-arg constructor,
  proper switch-based discriminant encoding
- Update 2 test files to use full XdrOfferEntry constructor

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ith BigInteger support

Replace hand-written implementations with generated code using BigInteger
FIELD_TYPE_OVERRIDE for feeCharged (int64). Add name overrides so both
types share XdrTransactionResultResult and XdrTransactionResultExt inner
types. Remove orphaned XdrInnerTransactionResultResult class. Update test
call sites to use full constructors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Generate *Base.php for XdrDecoratedSignature, XdrMuxedAccountMed25519,
XdrClaimableBalanceID, XdrContractExecutable, and XdrHostFunction.
Add encode/decode overrides in wrappers for hex conversion and
XdrDataValueMandatory handling. Add TYPE_OVERRIDES for SignatureHint
and Signature typedefs, FIELD_OVERRIDES for field name mappings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Regenerate XdrSCEnvMetaEntry, XdrOperationResult, XdrOperation, and
XdrMuxedAccount using the XDR code generator, removing them from
SKIP_TYPES. Update field overrides for XdrOperationResult (code→resultCode,
tr→resultTr). Add decode() override in XdrMuxedAccount wrapper for
constructor signature mismatch with generated base.

Update SorobanContractInfo to reflect the XDR spec change where
SCEnvMetaEntry.interfaceVersion changed from uint64 to a struct
with protocol and preRelease fields.

BREAKING CHANGES:
- SorobanContractInfo: `envInterfaceVersion` property removed, replaced
  with `envMetaProtocol` and `envMetaPreRelease`. Constructor signature
  changed from (int, array, array) to (int, int, array, array).
- XdrSCAddress: `getClaimableBalanceId()` return type changed from
  `?string` to `?XdrClaimableBalanceID`. Callers must use
  `->getClaimableBalanceId()?->getHash()` to get the hex string.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…KIP_TYPES

Add :optional_array encode/decode style to handle union arms where
Optional * wraps an array typedef (e.g., SCVec *vec where SCVec = SCVal<>).
This enables the generator to produce correct inline array loops with
optional presence flags.

Add TYPE_OVERRIDES for SCVec, SCBytes, SCString typedefs so they resolve
to native PHP types instead of generating wrapper classes. Add
FIELD_TYPE_OVERRIDE for XdrSCVal.bytes (XdrDataValueMandatory) and
FIELD_OVERRIDE for nonce_key → nonceKey.

Add xdr-generator vendor/ and .bundle/ to .gitignore.

SKIP_TYPES reduced from 10 to 9. All remaining are permanent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…SKIP_TYPES 9 → 6)

XdrAccountID: BASE_WRAPPER with StrKey conversion deferred to encode().
XdrAccountEntry: generated with BigInteger balance, Thresholds override to
string. Constructor reorders inflationDest as optional last param (breaking).
XdrAccountEntryV2: generated with new elements_optional support for array
fields whose element type is an optional typedef (SponsorshipDescriptor =
AccountID*). Per-element presence flags in encode/decode loops.

Generator changes:
- Add elements_optional flag for array-of-optional-typedef patterns
- Distinguish from truly optional array fields via m.type.sub_type check
- Add XdrThresholds → string to TYPE_OVERRIDES

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…S 6 → 4)

Generate base classes for XdrManageDataOperation and XdrTransactionV0,
with wrappers preserving existing public API to avoid breaking changes.

XdrManageDataOperation wrapper maintains getKey()/getValue() returning
XdrDataValue. XdrTransactionV0 wrapper preserves custom constructor
defaults and getSequenceNumber() getter. The generated base also fixes
a bug in the hand-written encode() which was missing seqNum and memo.

Add XdrDataValue TYPE_OVERRIDE mapping typedef to string so the
generator correctly resolves DataValue references in other types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Break incorrect inheritance where XdrChangeTrustAsset and XdrTrustlineAsset
extended XdrAsset. Per the XDR spec these are independent unions sharing
the same discriminant but are not subtypes of each other.

- Generate XdrAsset, XdrChangeTrustAssetBase, XdrTrustlineAssetBase
- Add wrappers for XdrChangeTrustAsset and XdrTrustlineAsset with
  backward-compatible helpers (fromXdrAsset, getPoolId/setPoolId)
- Add Asset::fromXdrChangeTrustAsset() and Asset::fromXdrTrustlineAsset()
- AssetTypePoolShare::toXdr() now throws; use toXdrChangeTrustAsset()
- Update callers in ChangeTrustOperation and TxRep
- Add POOL_SHARE round-trip test for XdrChangeTrustAsset

BREAKING CHANGE: XdrChangeTrustAsset and XdrTrustlineAsset no longer
extend XdrAsset. Use Asset::fromXdrChangeTrustAsset() for ChangeTrust
operations and Asset::fromXdrTrustlineAsset() for trustline ledger keys.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enum decode() methods now validate the integer value against known
constants using a switch statement and throw \InvalidArgumentException
on unknown values, matching the Flutter SDK's approach. This catches
data corruption and protocol version mismatches early instead of
silently creating enum objects with invalid values.

All 86 generated enum types are updated. Tests cover standard,
negative-value, and non-contiguous enum validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a Ruby test generator (generate_tests.rb) that parses XDR specs and
auto-generates PHPUnit tests for all XDR types. The generator produces
1,016 tests covering 8 patterns: round-trip encode/decode, invalid enum
decode, optional fields present, non-empty arrays, edge cases, getter/
setter validation, factory method tests, and enum factory methods.

Generate 158 new XDR PHP types from the spec (overlay, ledger, SCP,
transaction history, survey, etc.) to complement the 309 existing
hand-written types.

Remove ~5,600 lines of redundant round-trip tests from hand-written
test files — these are now covered by the auto-generated suite with
stronger assertions.

XDR test coverage: 85.2% lines (up from 72.4%), 76.2% methods
(up from 47.6%). Total unit tests: 3,377.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove wrapper/base class pairs for types where the wrapper only
provided constructor defaults. Callers updated to pass explicit values.

Eliminated wrappers:
- XdrSorobanTransactionData (empty wrapper)
- XdrPreconditionsV2 (defaulted optional params)
- XdrMemo (VALUE_TEXT_MAX_SIZE constant already in StellarConstants)
- XdrManageOfferResult (empty wrapper, no callers)
- XdrFeeBumpTransactionInnerTx (2-arg constructor shortcut)
- XdrFeeBumpTransaction (defaulted ext param)

BASE_WRAPPER_TYPES reduced from 39 to 33.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The generator's name_overrides.rb incorrectly collapsed two structurally
different XDR unions into one PHP class. The outer TransactionResult union
has FEE_BUMP_INNER_SUCCESS/FAILED arms (with InnerTransactionResultPair)
that the inner InnerTransactionResult union lacks. This caused fee bump
transaction results to decode incorrectly, making isSuccessful() always
return false for fee bump transactions.

Fix: remove the collapsing override so both classes are generated
separately. Add field override for consistent resultCode naming.

Also fixes AssetTypePoolShare passing extra arg to union constructor
(XdrLiquidityPoolParameters) which left constantProduct null.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The generator had overwritten the hand-written file, losing the
forVoidCase() and forTupleCase() factory methods. Add the type to
BASE_WRAPPER_TYPES so the generator produces a Base class while
the wrapper preserves the factory methods.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add GitHub Actions workflow with snapshot tests, drift detection,
  and validation jobs for the XDR generator
- Add 8 snapshot golden files covering all generator patterns
  (enum, struct, union, base-wrapper, extension points)
- Add xdr-generate-tests Makefile target
- Fix validate_generated_types.rb (broken parse call, missing
  MEMBER_PREFIX_STRIP support, dedup for collapsed types)
- Simplify generator: extract resolve_class_info helper, consolidate
  3 typedef renderers into 1, extract build_typed_arm helper, remove
  dead code (-97 net lines, identical output)
- Fix XdrThresholds.php incorrectly deleted by xdr-clean-generated
- Untrack internal working docs (bugs.md, learnings.md, progress.md)
- Add README.md for the XDR generator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov
Copy link

codecov bot commented Mar 10, 2026

Codecov Report

❌ Patch coverage is 85.31130% with 729 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.90%. Comparing base (9cffb2c) to head (5b88dbd).
⚠️ Report is 53 commits behind head on main.

Files with missing lines Patch % Lines
Soneso/StellarSDK/Xdr/XdrHostFunctionBase.php 42.59% 31 Missing ⚠️
Soneso/StellarSDK/Xdr/XdrContractCodeEntryV1.php 0.00% 22 Missing ⚠️
Soneso/StellarSDK/Xdr/XdrContractEventBodyV0.php 0.00% 21 Missing ⚠️
Soneso/StellarSDK/Xdr/XdrLedgerCloseMeta.php 59.57% 19 Missing ⚠️
Soneso/StellarSDK/Xdr/XdrConfigSettingEntry.php 78.57% 18 Missing ⚠️
Soneso/StellarSDK/Xdr/XdrLedgerCloseMetaV1.php 81.60% 16 Missing ⚠️
Soneso/StellarSDK/Xdr/XdrBucketEntry.php 62.50% 15 Missing ⚠️
Soneso/StellarSDK/Xdr/XdrHotArchiveBucketEntry.php 62.50% 15 Missing ⚠️
Soneso/StellarSDK/Xdr/XdrLedgerCloseMetaV2.php 82.89% 13 Missing ⚠️
Soneso/StellarSDK/Xdr/XdrLedgerEntryChange.php 51.85% 13 Missing ⚠️
... and 177 more
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##               main      #72      +/-   ##
============================================
+ Coverage     83.89%   85.90%   +2.01%     
- Complexity    10289    15245    +4956     
============================================
  Files           811      982     +171     
  Lines         25263    32772    +7509     
============================================
+ Hits          21194    28153    +6959     
- Misses         4069     4619     +550     
Files with missing lines Coverage Δ
Soneso/StellarSDK/AssetTypeCreditAlphanum12.php 92.85% <ø> (ø)
Soneso/StellarSDK/AssetTypeCreditAlphanum4.php 92.85% <ø> (ø)
Soneso/StellarSDK/BumpSequenceOperation.php 100.00% <100.00%> (ø)
Soneso/StellarSDK/ChangeTrustOperation.php 100.00% <100.00%> (ø)
Soneso/StellarSDK/FeeBumpTransaction.php 85.71% <100.00%> (+0.42%) ⬆️
Soneso/StellarSDK/Memo.php 98.75% <100.00%> (ø)
Soneso/StellarSDK/SEP/WebAuth/WebAuth.php 85.54% <100.00%> (ø)
Soneso/StellarSDK/Soroban/Address.php 86.11% <100.00%> (ø)
Soneso/StellarSDK/Soroban/SorobanContractInfo.php 96.49% <100.00%> (+0.06%) ⬆️
...oneso/StellarSDK/Soroban/SorobanContractParser.php 71.60% <100.00%> (+0.71%) ⬆️
... and 285 more

... and 198 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Generated test count increases from 1,013 to 1,115, restoring overall
line coverage from 80% back to 84%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Collaborator

@ngybnc ngybnc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed looks correct.

Regenerate XDR types from stellar/stellar-xdr#298 (Merge P26 XDR into curr).
Adds frozen ledger key and freeze bypass config settings, new result codes,
and updates TxRep config setting parsing for the new entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@christian-rogobete christian-rogobete merged commit 79947cf into main Mar 11, 2026
9 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

Development

Successfully merging this pull request may close these issues.

2 participants