Skip to content

Release 16.0.0

Choose a tag to compare

@github-actions github-actions released this 03 Oct 02:43
· 7 commits to refs/heads/main since this release
9440e88

16.0.0

Major release, recompile required. The first stable release since 15.1.0 (Protocol 26). It brings the SDK up to the network as it runs today: Protocol 27 (CAP-71 Soroban authorization, now defaulting to address-bound v2 credentials) and Protocol 28 (CAP-85 external executable references, CAP-83), and it reads Protocol 29 networks, which reached Mainnet on 2026-10-01 without changing the XDR. It adds SEP-7, SEP-12, SEP-38 and SEP-45 clients, ships multi-target packages (net10.0, net8.0, netstandard2.1) validated on .NET MAUI for Android, overhauls HTTP retry, and fixes a long list of Soroban RPC wire-format bugs. Everything first previewed in 16.0.0-beta is included. Binaries built against 15.x are not compatible, so recompile, then check the breaking changes below.

Breaking Changes

  • Soroban authorization: v2 credentials by default (#244, #187):
    • StellarRpcServer.SimulateTransaction now sends useUpgradedAuth: true unless told otherwise, so recording-mode simulation returns SorobanAddressCredentialsV2 entries where it returned legacy SorobanAddressCredentials. V2 signatures are bound to the signer's address and cannot be replayed against another account.
    • If you sign with SorobanAuthorization.AuthorizeEntry, nothing changes: it signs whichever variant simulation returned. SorobanAddressCredentialsV2 is a sibling type, not a subclass, so an is/as SorobanAddressCredentials check silently stops matching v2 entries. A hand-rolled signer must build its payload with SorobanAuthorization.BuildAuthorizationEntryPreimageHash, which picks the preimage from the entry.
    • To keep legacy entries (for a co-signer that cannot sign v2 yet), pass useUpgradedAuth: false to SimulateTransaction, or SorobanCredentialsVersion.V1 to AuthorizeEntry. Legacy signatures are not address-bound. Stellar RPC plans to drop the opt-out in a future protocol.
    • The useUpgradedAuth parameter (#209) changes the CLR signature of SimulateTransaction, so code compiled against an earlier release throws MissingMethodException until recompiled.
    • SorobanCredentials.ToXdr() is now abstract; external subclasses must override it. SorobanSourceAccountCredentials.ToSorobanCredentialsXdr() and SorobanAddressCredentials.ToSorobanCredentialsXdr() are removed: call ToXdr().
  • Protocol 28 XDR (#252): SCValType, ContractExecutableType and StellarValueType gain members (SCV_EXECUTABLE_TAG, CONTRACT_EXECUTABLE_EXTERNAL_REF, STELLAR_VALUE_EMPTY_TX_SET). Values that failed to decode with InvalidDataException now decode, so code that switches over these enums, or over SCVal / ContractExecutable subclasses, meets cases it has not seen before.
  • Stellar RPC errors are thrown, not swallowed (#217, #218):
    • Every StellarRpcServer method throws the new SorobanRpcException (with Code, ErrorMessage and ErrorData) on a JSON-RPC error, where it returned null. Replace null checks on the result with a catch.
    • A response body holding the JSON literal null throws ClientProtocolException instead of a NullReferenceException.
    • SorobanRpcResponse<T>.Id is now string?, as JSON-RPC allows a null id on an error response.
  • Simulation responses match the RPC contract (#220, #221, #222, #234):
    • SimulateTransactionResponse.MinResourceFee is now long? (was uint?): fees above ~429 XLM failed to deserialize. Transaction.AddResourceFee takes a long and throws OverflowException instead of wrapping to a tiny fee.
    • RestorePreamble.SorobanTransactionData no longer throws on every access, and is typed SorobanTransactionData?.
    • StateChanges, Results, Events and Results[i].Auth reject null elements; LedgerEntryChange.Type and RestorePreamble.MinResourceFee are required; LedgerEntryChange.Key is now string?, because the server omits an empty key.
    • SorobanAuthorization is now [JsonIgnore]; it is derived from Results[0].Auth, which is still serialized.
    • The decoded XDR properties (SorobanAuthorization, SorobanTransactionData, RestorePreamble.SorobanTransactionData) throw InvalidDataException for a malformed blob, where they threw FormatException or IOException.
  • Transaction statuses are parsed strictly (#220, #225, #235, #236):
    • getTransaction/getTransactions statuses, sendTransaction statuses and Horizon's async tx_status accept only their exact wire strings. A bare number was mapped by position, so a malformed "status": 1 read as SUCCESS; it is now rejected with JsonException.
    • TransactionInfo.Status, SendTransactionResponse.Status and SendTransactionResponse.Hash are required, so a response missing them fails instead of reading as NOT_FOUND or PENDING.
    • The three status enums carry type-level converters, so they keep their wire format even when serialized on their own with your own JsonSerializerOptions.
  • getEvents request shape (#219, #220): GetEventsRequest.EventFilter.Type is now the [Flags] enum EventFilterType (System, Contract) instead of a string, since the server accepts only those literals. PaginationOptions.Cursor and .Limit are now properties instead of fields, which is what finally puts them on the wire.
  • TransactionInfo metadata (#228): ResultValue, WasmHash and CreatedContractId now read the return value from V3 metas as well as V4, and return null instead of throwing when the meta cannot be decoded. TransactionMeta still returns null for a bad payload, but no longer swallows unrelated failures such as OutOfMemoryException.
  • AuthMode serialization (#209): serializing an AuthMode value directly now writes the wire spelling ("enforce", "record", "record_allow_nonroot"). The authMode request field was fixed separately (see Bug Fixes).
  • HTTP retry overhaul (#184):
    • The ForSoroban() / ForHorizon() presets retry transient HTTP status codes (408/429/500/502/503/504) on POST as well, since Stellar submission is idempotent. Do not use them for SEP clients, whose POSTs are not.
    • HTTP 429 and 503 throw the new TooManyRequestsException and ServiceUnavailableException.
    • ForSorobanPolling() is deprecated in favour of ForSoroban().
  • Stricter JSON (#181, #183, #201):
    • Duplicate JSON properties throw JsonException instead of last-value-wins, on every target framework and in the SDK's hand-written converters too.
    • The shared JsonSerializerOptions are frozen; clone them before modifying.
    • The SDK references the standalone System.Text.Json 10 package on net8.0 and netstandard2.1.
    • The Asset, AssetAmount, Reserve and LiquidityPoolClaimableAssetAmount converters throw JsonException for malformed values, where they leaked ArgumentException or AssetCodeLengthInvalidException.
  • Stricter XDR decoding (#189): XdrDataInputStream throws EndOfStreamException on truncated input (was IndexOutOfRangeException).
  • KeyPair exceptions (#202): constructors and byte-array factories throw ArgumentException / ArgumentNullException for bad key material on every target framework (was NSec's FormatException). Sign / SignDecorated without a private key throw InvalidOperationException (was Exception).

Features

  • Protocol 28 (#252):
    • CAP-85 external executable references: SCExecutableTag, ContractExecutableExternalRef, CreateContractOperation.FromExternalRef to deploy from one, and StellarRpcServer.GetExternalRefWasmHash to resolve one, with ExternalRefNotFoundException telling a missing tag entry from an archived one.
    • CAP-83 STELLAR_VALUE_EMPTY_TX_SET decodes.
    • Generated XDR string types (SCString, SCSymbol, String32, String64) keep the raw wire bytes in a new InnerBytes property, so non-UTF-8 strings such as binary executable tags round-trip unchanged. InnerValue remains as a UTF-8 view.
    • Known-answer tests against @stellar/stellar-sdk 17.2.0, and live Testnet tests.
  • Protocol 27 (CAP-71) Soroban authorization (#187, #209): SorobanAddressCredentialsV2 and delegated credentials (SorobanAddressCredentialsWithDelegates, SorobanDelegateSignature); signing helpers AuthorizeEntry, AuthorizeEntryWithDelegates, BuildWithDelegatesEntry and BuildAuthorizationEntryPreimageHash; the ISorobanEntrySigner abstraction with KeyPairEntrySigner; and the useUpgradedAuth simulation flag. Signing is verified byte for byte against the JS SDK, and end to end on Testnet.
  • SEP-7 URI scheme for payment requests and delegated signing (#238): UriScheme, Sep7Uri, signing and verification, and submission to a callback.
  • SEP-12 KYC API client (#239): KycService for every customer, file and callback endpoint, and KycCallbackSignature to verify anchor callbacks.
  • SEP-38 anchor RFQ client (#243): QuoteService for /info, /prices, /price and /quote.
  • SEP-45 web authentication for contract accounts (#190): ClientWebAuthContract and Sep45Challenge.
  • The SDK now implements 9 SEPs (SEP-1, 6, 7, 9, 10, 12, 24, 38 and 45), each with a compatibility matrix at 100% field coverage. The SEP-12 and SEP-38 clients take the JWT from the SEP-10 or SEP-45 client.
  • Multi-target packages (#195): both packages ship net10.0, net8.0 and netstandard2.1 builds, so the SDK installs in Unity 2022.3+, Tizen and other netstandard2.1 hosts.
  • KeyPair implements IDisposable to release its native signing key deterministically (#195).
  • HTTP retry (#184): ForSoroban() and ForHorizon() presets that honour Retry-After, and new HttpResilienceOptions settings (RetryHttpStatusCodes, RetryHttpMethods, RespectRetryAfter, MaxRetryAfterDelay).
  • GetLatestLedgerResponse gains CloseTime, HeaderXdr and MetadataXdr, and GetHealthResponse gains LatestLedgerCloseTime and OldestLedgerCloseTime (#198).

Bug Fixes

  • SimulateTransaction sent authMode in a casing Stellar RPC rejects, so every call that set it failed, in every release since 14.0.0 (#209).
  • getEvents dropped the caller's cursor and limit, so paging was impossible (#219).
  • JSON-RPC errors were returned as null results, hiding the server's message (#217, #218).
  • MinResourceFee above uint.MaxValue stroops broke deserialization of the whole simulation response (#221).
  • RestorePreamble.SorobanTransactionData threw on every access, so the archived-entry restore flow did not work (#222).
  • TransactionInfo.ResultValue ignored V3 transaction metas, reporting no return value for contract calls recorded before Protocol 23 (#228).
  • Status enums could read a malformed or missing value as a real status (#225, #235, #236).
  • A getLedgerEntries response with a null entry made LedgerEntries, LedgerKeys and GetAccount throw NullReferenceException; it now fails with JsonException (#252).
  • SEP-45 rejected challenge and token responses that start with a UTF-8 byte order mark (#239).
  • contract_credited / contract_debited effects had no response mapping (#179).
  • Converters leaked FormatException, OverflowException and ArgumentOutOfRangeException for malformed input instead of JsonException, and the Asset and Reserve converters mis-read unknown nested properties (#201, #232).
  • KeyPair construction-time validation lost during the multi-target change is restored (#202); Util.Hash no longer leaks a SHA256 instance per call (#195).
  • The assemblies carry the release version: 15.x DLLs reported 12.0.0 (SDK) and 10.0.0 (XDR), and sent X-Client-Version: 12.0.0.0 with every request (#255).
  • The documentation build (#178).

Security

  • Converters that hand-parse JSON reject duplicate properties, closing a parser-differential gap where those fields were last-value-wins (#201).
  • The status and event-filter converters clamp and escape a rejected value in their exception messages instead of copying it whole, so a hostile payload cannot bloat or forge log lines (#232).
  • The SEP-7, SEP-12 and SEP-38 clients use https only, cap response sizes, don't follow redirects, and reject duplicate JSON properties.

Documentation

  • .NET MAUI compatibility report: Android emulator and four physical devices, trimming, and workarounds (#242).
  • SEP compatibility matrices (#191, plus the SEP-7, SEP-12 and SEP-38 PRs); the RPC and Horizon matrices are re-pinned to v28.0.1 (#198).
  • The 61 XML documentation warnings are fixed, and new ones now fail the build (#254).

Maintenance

  • Testnet integration test suite, run in CI on every push to main (#185, #196).
  • RespectNullableAnnotations for JSON deserialization (#182) and FrozenDictionary lookup tables in the JSON converters (#180).
  • NuGet Trusted Publishing for the release pipeline (#203).
  • The XDR generator names any feature-gated #ifdef block left in a schema (#252).
  • Agent skills to verify the SDK at its package boundary and the MAUI harness (#253).

Known limitations

  • iOS has not been validated. The MAUI validation covered Android only; iOS simulator and device behaviour remain untested.
  • SSE streaming on Android with the default HTTP handler delivers events late (about 47 s in our runs). Give the stream's request builder an SseEventSource that uses a SocketsHttpHandler; the MAUI report has the snippet.
  • .NET 9 MAUI apps resolve the net8.0 build, whose libsodium has no Android binaries: add a reference to NSec.Cryptography 26.4.0. .NET 10 MAUI apps need nothing extra.

Full Changelog: 15.1.0...16.0.0 · CHANGELOG