Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@
- A hand-rolled signer that hardcodes the legacy `ENVELOPE_TYPE_SOROBAN_AUTHORIZATION` preimage now produces signatures the network rejects — build the payload with `Auth.buildAuthorizationEntryPreimage`, which picks the address-bound preimage off the entry. SDK-driven signing (`Auth.authorizeEntry`, `AssembledTransaction.signAuthEntries`, `ContractClient`) needs no change.

### Update
- feat: add CAP-85 external executable reference support. A contract instance can now hold a `CONTRACT_EXECUTABLE_EXTERNAL_REF` executable — an owner contract plus an owner-scoped tag — instead of its own Wasm hash. The owner publishes the Wasm hash in a persistent contract data entry keyed by `SCV_EXECUTABLE_TAG(tag)`, so it can upgrade every contract referencing that tag at once. ([#814](https://github.com/lightsail-network/java-stellar-sdk/issues/814))
- New `SorobanServer.getExternalRefWasmHash(ContractExecutableExternalRef)` resolves a reference to the 32-byte Wasm hash it names, with a single `getLedgerEntries` call; the owner contract is not invoked.
- `SorobanServer.getContractWasm` — and so `getContractMeta`, `getContractSpec`, and `getContractInfo` — follows an external reference automatically, at the cost of that one extra request.
- New `ExternalRefNotFoundException` (a `ContractIntrospectionException`) is thrown when the tag entry is missing or archived. An unresolvable reference — one whose owner is not a contract, say, since only a contract can hold the tag entry — is rejected before any request is made: `getExternalRefWasmHash` throws `IllegalArgumentException`, where the reference is the caller's own argument, while `getContractWasm` reports it as `ContractWasmRetrievalException`, where it came off the ledger instead. Every `getContractWasm` failure therefore stays catchable as `ContractIntrospectionException`.
- New `InvokeHostFunctionOperation.createContractFromExternalRefOperationBuilder(...)` builds a `CREATE_CONTRACT_V2` operation that deploys from a reference instead of a Wasm hash, taking the owner contract plus a `String` or `byte[]` tag.
- New `Scv.toExecutableTag(String)` / `Scv.toExecutableTag(byte[])` / `Scv.fromExecutableTag(SCVal)` for the `SCV_EXECUTABLE_TAG` value. A tag is an unbounded `SCString` that need not be valid UTF-8 and identifies the code being deployed, so it is never decoded leniently: `fromExecutableTag` returns raw bytes, ledger keys are built from the original bytes, and binary tags are passed through undecoded.
- New `Util.decodeUtf8(byte[])` returns `Optional<String>` — the text when the bytes are valid UTF-8, empty otherwise. Use it to display a tag, falling back to the raw bytes, instead of a lenient decode that would render two distinct tags identically.
- `ScvComparator` orders the new `SCV_EXECUTABLE_TAG` value (lexicographic unsigned bytes) and the new executable arm (by owner, then tag), so a contract instance holding an external reference sorts correctly.
- CAP-85 requires protocol 28. On an earlier network the new executable arm is not accepted, so deploying from a reference fails at submission; the read paths never encounter one, since no contract can hold a reference there.
- feat: add `useUpgradedAuth` opt-outs at every layer that simulates: `SorobanServer.prepareTransaction(Transaction, boolean)`, `AssembledTransaction.simulate(boolean, boolean)`, and the `ContractClient.invoke` overload taking `useUpgradedAuth`. Derived transactions inherit the choice — the restore transaction `AssembledTransaction` builds during automatic restoration simulates with the same flag rather than falling back to the default. ([#814](https://github.com/lightsail-network/java-stellar-sdk/issues/814))
- feat: add a `useUpgradedAuth` opt-out to `Sep45Challenge.buildChallengeAuthorizationEntries`. SEP-45 does not specify a credential format, and the challenge entries are signed by a remote client rather than by the caller, so an anchor serving clients whose SDK cannot sign the address-bound payload can pass `false` to keep issuing legacy challenges. ([#814](https://github.com/lightsail-network/java-stellar-sdk/issues/814))

Expand Down
1 change: 1 addition & 0 deletions skills/java-stellar-sdk/references/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ factories for every host function:
| `InvokeHostFunctionOperation.invokeContractFunctionOperationBuilder(contractId, functionName, parameters)` | Call a contract function |
| `InvokeHostFunctionOperation.uploadContractWasmOperationBuilder(wasmBytes)` | Upload Wasm bytecode |
| `InvokeHostFunctionOperation.createContractOperationBuilder(wasmId, address, constructorArgs, salt)` | Instantiate a contract |
| `InvokeHostFunctionOperation.createContractFromExternalRefOperationBuilder(owner, tag, address, constructorArgs, salt)` | Instantiate a contract from a CAP-85 external executable reference (owner contract + tag) instead of a Wasm hash |
| `InvokeHostFunctionOperation.createStellarAssetContractOperationBuilder(asset)` | Deploy the SAC for a classic asset |
| `ExtendFootprintTTLOperation` | Extend ledger entry TTL |
| `RestoreFootprintOperation` | Restore archived state |
Expand Down
33 changes: 33 additions & 0 deletions skills/java-stellar-sdk/references/soroban.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,41 @@ try (SorobanServer server = new SorobanServer("https://soroban-testnet.stellar.o
server.getContractInfo(contractId); // SEP-48 (interface spec + meta)
server.getContractMeta(contractId); // SEP-46
server.getContractSpec(contractId); // SEP-48
server.getContractWasm(contractId); // raw Wasm; follows a CAP-85 external ref
server.getContractWasmByHash(wasmHash);
server.getExternalRefWasmHash(ref); // CAP-85: resolve a reference to a Wasm hash
}
```

### CAP-85 external executable references

A contract's instance can hold a `CONTRACT_EXECUTABLE_EXTERNAL_REF` executable instead of its own
Wasm hash: an owner contract plus an owner-scoped tag. The owner publishes the Wasm hash in a
*persistent* contract data entry keyed by `SCV_EXECUTABLE_TAG(tag)`, so it can upgrade every
contract referencing that tag at once.

Every Wasm-reading method above resolves the reference for you (one extra `getLedgerEntries`
call). To resolve one by hand:

```java
ContractExecutable executable = instance.getExecutable();
if (executable.getDiscriminant() == ContractExecutableType.CONTRACT_EXECUTABLE_EXTERNAL_REF) {
byte[] wasmHash = server.getExternalRefWasmHash(executable.getExternal_ref());
byte[] wasm = server.getContractWasmByHash(wasmHash);
}
```

A tag is an unbounded `SCString` and need not be valid UTF-8. Keep it as `byte[]`; only show it as
text when `Util.decodeUtf8(tag)` returns a value, and show the raw bytes otherwise. Never decode it
leniently — the tag is half of what identifies the code, so two distinct tags would render alike.
The owner must be a contract address; the SDK rejects anything else before making a request.
`ExternalRefNotFoundException` means the tag entry is missing or archived. An unresolvable reference
surfaces differently depending on where it came from: `getExternalRefWasmHash` throws
`IllegalArgumentException`, since the reference is your own argument, while `getContractWasm` (and
so `getContractMeta` / `getContractSpec` / `getContractInfo`) reports `ContractWasmRetrievalException`,
since there it came off the ledger — so every failure of those stays catchable as
`ContractIntrospectionException`.

### Manual submit loop

```java
Expand Down Expand Up @@ -181,6 +213,7 @@ assembled.signAndSubmit(submitter, false);
`ContractClient` only invokes functions. To upload Wasm or create a contract, build an
`InvokeHostFunctionOperation` with `SorobanServer` directly (see `operations.md`):
`uploadContractWasmOperationBuilder(wasmBytes)`, `createContractOperationBuilder(...)`,
`createContractFromExternalRefOperationBuilder(owner, tag, address, ctorArgs, salt)` (CAP-85),
`createStellarAssetContractOperationBuilder(asset)`. Prepare, sign, send, then read the Wasm ID
/ contract ID from the transaction meta.

Expand Down
12 changes: 12 additions & 0 deletions skills/java-stellar-sdk/references/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ Contract client lifecycle (in `org.stellar.sdk.contract.exception`, all extend
`SendTransactionFailedException`, `TransactionStillPendingException`,
`TransactionFailedException`, `NeedsMoreSignaturesException`, `NoSignatureNeededException`.

Contract introspection (in `org.stellar.sdk.contract.exception`, all extend
`ContractIntrospectionException` → `SdkException`) — raised by `ContractMeta`, `ContractSpec`,
`ContractInfo` and the `SorobanServer` methods that read a contract's Wasm:
- `ContractInstanceNotFoundException` — the contract instance ledger entry does not exist.
- `ContractCodeNotFoundException` — the contract code entry is missing or archived.
- `StellarAssetContractHasNoWasmException` — the contract is a SAC, which has no Wasm on-chain.
- `ExternalRefNotFoundException` — the CAP-85 tag entry that an external executable reference points
at is missing or archived.
- `ContractWasmRetrievalException` — the RPC response held unexpected ledger entry data, including
an external executable reference that cannot be resolved.
- `InvalidWasmException` — the Wasm was fetched but could not be parsed.

Other:
- `AccountRequiresMemoException` — destination requires a memo (SEP-29).
- `InvalidSep10ChallengeException`, `InvalidSep45ChallengeException` — challenge validation.
Expand Down
2 changes: 2 additions & 0 deletions skills/java-stellar-sdk/references/xdr_scval.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Scv.toUint256(BigInteger.valueOf(7)); Scv.toInt256(BigInteger.valueOf(-7));
Scv.toTimePoint(BigInteger.valueOf(1700000000)); Scv.toDuration(BigInteger.valueOf(3600));
Scv.toBytes(new byte[] {1, 2});
Scv.toString("hello"); Scv.toSymbol("increment");
Scv.toExecutableTag("v1"); // CAP-85 tag; byte[] overload for a binary tag
Scv.toAddress("G..."); // account or contract ("C...") address
Scv.toVec(List.of(Scv.toUint32(1L), Scv.toUint32(2L)));
Scv.toMap(Map.of(Scv.toSymbol("k"), Scv.toUint32(1L)));
Expand All @@ -31,6 +32,7 @@ Scv.toMap(Map.of(Scv.toSymbol("k"), Scv.toUint32(1L)));
Scv.fromUint32(v); // -> long
Scv.fromInt128(v); // -> BigInteger
Scv.fromString(v); // -> byte[]; new String(bytes, StandardCharsets.UTF_8) for text
Scv.fromExecutableTag(v); // -> byte[]; Util.decodeUtf8(bytes) for text, raw bytes if not UTF-8
Scv.fromSymbol(v); // -> String
Scv.fromAddress(v); // -> Address
Scv.fromVec(v); // -> Collection<SCVal>
Expand Down
Loading
Loading