Skip to content

contract: add read-only Soroban contract invocation#5946

Merged
marwen-abid merged 6 commits into
feat/contract-layerfrom
feat/EN-51-contract-invoke-read
Jul 9, 2026
Merged

contract: add read-only Soroban contract invocation#5946
marwen-abid merged 6 commits into
feat/contract-layerfrom
feat/EN-51-contract-invoke-read

Conversation

@marwen-abid

Copy link
Copy Markdown
PR Checklist

PR Structure

  • This PR has reasonably narrow scope (if not, break it down into smaller PRs).
  • This PR avoids mixing refactoring changes with feature changes (split into two PRs
    otherwise).
  • This PR's title starts with name of package that is most changed in the PR, ex.
    services/friendbot, or all or doc if the changes are broad or impact many
    packages.

Thoroughness

  • This PR adds tests for the most critical parts of the new functionality or fixes.
  • I've updated any docs (developer docs, .md
    files, etc... affected by this change). Take a look in the docs folder for a given service,
    like this one.

Release planning

  • I've reviewed the changes in this PR and if I consider them worthwhile for being mentioned on release notes then I have updated the relevant CHANGELOG.md within the component folder structure. For example, if I changed horizon, then I updated (services/horizon/CHANGELOG.md. I add a new line item describing the change and reference to this PR. If I don't update a CHANGELOG, I acknowledge this PR's change may not be mentioned in future release notes.
  • I've decided if this PR requires a new major/minor version according to
    semver, or if it's mainly a patch change. The PR is targeted at the next
    release branch if it's not a patch change.

What

Adds a new contract package providing a higher-level surface for invoking Soroban contracts. This PR is intentionally scoped to the read-only path: it carries a generic contract call from build → simulate → decoded return value, with no signer and no submission.

Adds:

  • contract.Client (New, Invoke) — generic invocation against any contract. Invoke builds an InvokeHostFunction op, simulates it, and returns an AssembledTransaction ready to read. Configured with functional options (WithDefaultSource/WithSource, WithBaseFee, WithResourceFeeMultiplier, WithMemo, WithTimeBounds). Read-only calls need no funded source — they simulate against a synthetic null account.
  • contract.AssembledTransaction (Simulate) — models the Soroban transaction lifecycle as an explicit, ctx-driven type. Simulate folds the simulated SorobanData footprint, auth entries, and (verbatim, un-padded) minResourceFee back into a rebuilt transaction.
  • Read helpersResult() returns the simulated ScVal for view calls; IsReadCall, NeedsNonInvokerSigningBy, and RequiresEnforcingResimulation classify the simulation outcome.
  • Typed error treeError{Kind} with errors.Is sentinels (ErrInvalidArgs, ErrSourceAccountFailed, ErrSimulationFailed, ErrNotYetSimulated).
  • xdr SCVal buildersScv* one-liner, non-panicking constructors plus ScAddressFromStrkey, validating symbols, 128-bit ranges, and strkey payloads up front.

Why

Ultimately, we're building towards making it easier to send payments using Go. This is the first step.
It is deliberately landed read-only first so the core xdr → client → simulate → decode path can be reviewed against a real consumer and tests before the write path is built on top.

Known limitations

Scoped to the read path; the following are planned for follow-up PRs:

  • No writes. No signer, no sign/send/poll. Result() on a write call returns ErrNotYetSimulated.
  • Untyped args. Arguments are raw []xdr.ScVal; marshaling native Go values into arguments is not here yet.
  • No auto-restore. A RestorePreamble from simulation is surfaced as a KindSimulationFailed error rather than transparently restored + re-simulated.
  • Source accounts must be ed25519 (G…) strkeys; muxed/custom-sequenced sources require building with txnbuild directly.

@marwen-abid marwen-abid self-assigned this Jun 5, 2026
@marwen-abid
marwen-abid force-pushed the feat/EN-51-contract-invoke-read branch from 282926c to 81f529e Compare June 5, 2026 15:17
@marwen-abid
marwen-abid marked this pull request as ready for review June 5, 2026 17:42
Copilot AI review requested due to automatic review settings June 5, 2026 17:42

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 81f529e125

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread contract/client.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new contract package that provides a higher-level, read-only API for invoking Soroban smart contracts via build → simulate → decode, plus a set of safe, non-panicking xdr ScVal builder helpers for constructing Soroban arguments.

Changes:

  • Added contract.Client and contract.AssembledTransaction to construct and simulate Soroban InvokeHostFunction transactions and expose simulation outputs.
  • Added typed error classification (contract.Error + ErrorKind + sentinels) and auth/result helpers for interpreting simulation outcomes.
  • Added xdr Scv* builder helpers (including ScAddressFromStrkey) with validation and extensive round-trip tests.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
xdr/scval_builders.go Adds validated, non-panicking Scv* helpers and ScAddressFromStrkey for building Soroban argument ScVals.
xdr/scval_builders_test.go Unit tests for address decoding, symbol validation, 128-bit range checks, and deterministic map encoding.
xdr/scval_builders_roundtrip_test.go Ensures Scv* builders round-trip through XDR marshal/unmarshal with stable type discriminants.
contract/result.go Adds AssembledTransaction.Result() for retrieving simulated return values for read calls.
contract/errors.go Introduces contract.Error/ErrorKind classification and errors.Is sentinels.
contract/errors_test.go Tests error formatting, unwrap behavior, and classifier semantics.
contract/client.go Adds contract.Client with functional options and Invoke() to build+simulate calls.
contract/client_test.go Tests client construction, invocation behavior, options, and error classification.
contract/auth.go Adds helpers to classify read calls and interpret simulation auth requirements.
contract/auth_test.go Tests auth helper behavior including enforcing resimulation detection and signer extraction.
contract/assembled_transaction.go Implements build/simulate lifecycle wrapper and simulation decoding/fee handling.
contract/assembled_transaction_test.go Tests simulation folding, error paths, resource fee behavior, and Result() branches.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread contract/client.go
Comment thread contract/assembled_transaction.go
Comment thread contract/assembled_transaction.go
Comment thread contract/client.go
Comment thread contract/auth.go Outdated
Comment thread contract/result.go
Comment thread contract/auth.go Outdated
Comment thread contract/auth.go Outdated
Comment thread contract/client.go
Comment thread contract/client.go
Comment thread contract/client.go
Comment thread xdr/scval_builders.go
Comment thread xdr/scval_builders.go
Comment thread contract/assembled_transaction.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b05a137731

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread contract/assembled_transaction.go Outdated

method, args := extractInvocation(params.Op.HostFunction)

tx, err := buildTx(params.SourceAccount, params.Op, params.BaseFee, params.Memo, params.Preconditions)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Default optional preconditions before building

When callers use the exported NewAssembledTransaction directly and leave AssembleParams.Preconditions at its zero value as documented optional, this call passes that zero value into txnbuild.NewTransaction; txnbuild.Preconditions.Validate rejects zero TimeBounds because they were not constructed with NewTimebounds, NewTimeout, or NewInfiniteTimeout. Client.Invoke sets infinite time bounds, so the tests cover only that path, but direct users of this constructor cannot assemble a transaction unless they know to supply preconditions despite the API marking them optional.

Useful? React with 👍 / 👎.

Comment thread contract/auth.go Outdated
Comment thread contract/auth.go
Adds a new contract package providing a higher-level surface for
invoking Soroban contracts, in a read-only form: it carries a generic
contract call from build -> simulate -> decoded return value. There is
no signer and no submission yet.

What lands:

- contract.Client (New, Invoke): generic invocation against any Soroban
  contract. Invoke builds an InvokeHostFunction op, simulates it, and
  returns an AssembledTransaction ready to read. Configured with
  functional options: WithDefaultSource/WithSource, WithBaseFee,
  WithResourceFeeMultiplier, WithMemo, WithTimeBounds. Read-only calls
  need no funded source — they simulate against a synthetic null account.

- contract.AssembledTransaction (Simulate): models the Soroban
  transaction lifecycle as an explicit, ctx-driven type. Simulate folds
  the simulated SorobanData footprint, auth entries, and verbatim
  minResourceFee back into a rebuilt transaction. A RestorePreamble is
  detected and reported as not-yet-supported rather than silently
  mishandled.

- Read helpers: Result() returns the simulated ScVal for view calls;
  IsReadCall, NeedsNonInvokerSigningBy, and RequiresEnforcingResimulation
  classify the simulation outcome.

- A typed error tree (Error{Kind}, ErrorKind, errors.Is sentinels) for
  the lifecycle stages exercised here (invalid args, source-account,
  simulation, not-yet-simulated).

- xdr SCVal builders (Scv*) and ScAddressFromStrkey: one-liner,
  non-panicking constructors for host-function arguments that validate
  symbols, 128-bit ranges, and strkey payloads up front.

Arguments are raw []xdr.ScVal for now. Native-value argument marshaling,
single-signer writes (signer + sign/send/poll), and auto-restore are not
included and will follow in later PRs.
@marwen-abid
marwen-abid force-pushed the feat/EN-51-contract-invoke-read branch from b05a137 to 9463cf1 Compare June 9, 2026 21:48

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9463cf165a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread contract/client.go Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: de31f2920a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread contract/auth.go
- NewAssembledTransaction: default a zero-value Preconditions.TimeBounds to
  NewInfiniteTimeout (matching Invoke) so the documented-optional field builds without a factory-constructed TimeBounds.
- validateSourceAddr: use keypair.ParseAddress, which length-checks the decoded payload, instead of the length-blind strkey.IsValidEd25519PublicKey.
- auth: note that RequiresEnforcingResimulation / NeedsNonSourceAccountSigningBy scope to Address/AddressV2; delegated credentials land with multi-party signing.
- Rename leftover NeedsNonInvokerSigningBy test names/comments.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cecae89bb7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread contract/auth.go
@marwen-abid
marwen-abid changed the base branch from main to feat/contract-layer June 17, 2026 20:54

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e8b33d156

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread contract/client.go
InvokeContract: &xdr.InvokeContractArgs{
ContractAddress: c.contractAddr,
FunctionName: *methodSym.Sym,
Args: args,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Copy invocation args before storing them

When a caller reuses or mutates the args slice after Invoke returns and then calls at.Simulate() again, the simulation request is encoded from the existing Built envelope while the rebuild uses a.op, whose InvokeContract.Args still aliases the caller's mutated slice. That can leave Simulation/ReturnValue describing the old call but Built containing different arguments, so copy the slice before assigning it to the operation.

Useful? React with 👍 / 👎.

@marwen-abid
marwen-abid merged commit fcfdf7f into feat/contract-layer Jul 9, 2026
11 checks passed
@marwen-abid
marwen-abid deleted the feat/EN-51-contract-invoke-read branch July 9, 2026 17:20
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.

1. Read-only contract invocation (contract.Client + AssembledTransaction)

4 participants