Skip to content

Add a client for Stellar Asset Contracts - #204

Merged
christian-rogobete merged 2 commits into
mainfrom
sac-toolkit
Nov 11, 2026
Merged

christian-rogobete merged 2 commits into
mainfrom
sac-toolkit

Conversation

@ngybnc

@ngybnc ngybnc commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator

SorobanClient::forClientOptions threw "Could not load contract info" for every Stellar Asset Contract, because a SAC has no wasm to read a spec from. The SDK also had no way to derive a SAC id from an asset.

This change:

  • embeds the upstream SAC spec as SACSpec, generated from a pinned commit by a standard-library Python tool that checks its sha256
  • adds Asset::getContractId() to derive an asset's SAC id on a given network
  • opens a SAC in forClientOptions with the embedded spec, and sends the same requests as before for wasm and external-reference contracts
  • adds SorobanClient::forSpecEntries() and a read-only mode that simulates without loading the source account and refuses to sign
  • adds SACClient with typed balance, transfer, transferAndSend, decimals, name and symbol, plus toBaseUnits() for decimal amounts

The new SACClientTest integration class passed against testnet on 2026-11-11 (UTC).

SorobanClient::forClientOptions threw "Could not load contract info"
for every Stellar Asset Contract, because a SAC has no wasm to read a
spec from. The SDK also had no way to derive a SAC id from an asset.

This change:
- embeds the upstream SAC spec as SACSpec, generated from a pinned
  commit by a standard-library Python tool that checks its sha256
- adds Asset::getContractId() to derive an asset's SAC id on a given
  network
- opens a SAC in forClientOptions with the embedded spec, and sends
  the same requests as before for wasm and external-reference
  contracts
- adds SorobanClient::forSpecEntries() and a read-only mode that
  simulates without loading the source account and refuses to sign
- adds SACClient with typed balance, transfer, transferAndSend,
  decimals, name and symbol, plus toBaseUnits() for decimal amounts

The new SACClientTest integration class passed against testnet on
2026-11-11 (UTC).
@codecov

codecov Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.51%. Comparing base (fb1224d) to head (15739ec).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##               main     #204      +/-   ##
============================================
+ Coverage     93.47%   93.51%   +0.03%     
- Complexity    21622    21684      +62     
============================================
  Files          1006     1007       +1     
  Lines         52424    52601     +177     
============================================
+ Hits          49003    49189     +186     
+ Misses         3421     3412       -9     
Files with missing lines Coverage Δ
Soneso/StellarSDK/Asset.php 80.19% <100.00%> (+2.17%) ⬆️
...ellarSDK/Soroban/Contract/AssembledTransaction.php 84.16% <100.00%> (+0.98%) ⬆️
...K/Soroban/Contract/AssembledTransactionOptions.php 100.00% <ø> (ø)
Soneso/StellarSDK/Soroban/Contract/SACClient.php 100.00% <100.00%> (ø)
...neso/StellarSDK/Soroban/Contract/SorobanClient.php 97.66% <100.00%> (+0.14%) ⬆️

... and 5 files with indirect coverage changes

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

Before this change, SorobanClient::forClientOptions() read the contract
instance itself, so a SorobanServer subclass that overrides
loadContractInfoForContractId() was no longer called. send() submitted
a read-only transaction whose signed field was set by hand. An amount
error echoed its input even when the input was a secret seed.

This change:
- Calls the server's loader first in forClientOptions() and reads the
  instance only when the loader returns nothing, to open a Stellar Asset
  Contract with the embedded spec.
- Refuses send() on a read-only transaction before any request.
- Names a secret seed passed as an amount by its kind only.
- Removes an error branch of transferAndSend() that polling never
  reaches and tests the polling error through the real path.
- Fails the integration test when the network reports no protocol
  version, and pins the full invocation of decimals(), name() and
  symbol() in the unit tests.

Unit suite and static analysis pass. The SACClientTest integration
class passed against testnet on 2026-11-11 (UTC).
@christian-rogobete

Copy link
Copy Markdown
Member

Tank you @ngybnc!

@christian-rogobete
christian-rogobete merged commit fa4ea5e into main Nov 11, 2026
16 checks passed
@christian-rogobete
christian-rogobete deleted the sac-toolkit branch November 11, 2026 17:41
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