Skip to content
Open
15 changes: 15 additions & 0 deletions evals/scenarios/smart-contracts/04-build-verification.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"skills": [
"smart-contracts"
],
"query": "I'm deploying my Soroban contract to mainnet next week. How do I make it so users can check that the deployed WASM really came from my GitHub repo?",
"expected_behavior": [
"Points at SEP-55 build verification — source_repo metadata in the WASM plus a GitHub build attestation — rather than improvising a scheme like publishing the hash in the README",
"Names either stellar contract build --meta source_repo=github:<owner>/<repo> or the soroban-build-workflow reusable workflow",
"If it reuses soroban-build-workflow, resolves the current release itself, reads release.yml at that commit rather than at the tag, and pins the full commit SHA rather than a version tag copied from documentation",
"Grants contents: write for publishing the release and id-token: write plus attestations: write for the attestation, without presenting all three as attestation requirements",
"Says to deploy the WASM attached to the release the workflow produced, because a local rebuild may not match the attested hash",
"States the limits: the source repo must be public to be checkable, and the attestation proves which workflow run and commit built the binary, not that the code is safe",
"Says the commit pin covers release.yml only, not the actions release.yml calls on mutable tags, rather than presenting the pin as closing the whole supply chain"
]
}
55 changes: 54 additions & 1 deletion skills/smart-contracts/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,59 @@ stellar contract invoke \

To upload WASM without instantiating (e.g. for factories or upgrades), use `stellar contract upload` (the older `stellar contract install` is a deprecated alias).

## Verify your build

Nothing on-chain ties a deployed WASM to its source. [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md) (Draft) closes that gap: build in GitHub Actions, stamp the source repository into the WASM's `contractmetav0` section, and publish a [GitHub build attestation](https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations/use-artifact-attestations) over the binary — anyone can then walk from the on-chain WASM hash back to the commit and the workflow run that produced it. Verifiers read the attestation from `api.github.com/repos/<owner>/<repo>/attestations/sha256:<wasm-hash>` unauthenticated, so the source repository has to be public for that "anyone" to hold. On the Free, Pro, and Team plans a private or internal repo cannot publish an attestation at all. Enterprise Cloud can, but only accounts with repository access read it, so no user of your contract can. Make it a standard step before **any** mainnet deploy; it costs one workflow file, so there's no reason to reserve it for user-facing contracts.

Two metadata entries carry it (`stellar contract build --meta key=value`, read back with `stellar contract info meta --wasm <file>`):

- `source_repo=github:<owner>/<repo>` — where the source lives
- `home_domain=<domain>` — domain serving your SEP-1 `stellar.toml`, for org/token lookup (optional; bare domain, no scheme or path)

[soroban-build-workflow](https://github.com/stellar-expert/soroban-build-workflow) does the whole job — build with the metadata, optimize, attest, publish a release with the WASM attached, and register the build with stellar.expert's contract validation.

Resolve its version yourself instead of copying a pin out of any documentation, this file included. Pins go stale, and the newest tag in that repo is not always a published release:

```bash
REPO=stellar-expert/soroban-build-workflow
TAG=$(gh release view --repo "$REPO" --json tagName --jq .tagName) # latest release
SHA=$(gh api "repos/$REPO/commits/$TAG" --jq .sha) # its commit
gh api "repos/$REPO/contents/.github/workflows/release.yml?ref=$SHA" \
-H "Accept: application/vnd.github.raw" # the file you will pin
```

Read `release.yml` at that SHA, never at the tag. A tag can move between the two steps, and then you review one commit and pin another. It should check out the tagged commit, build from source, and attest the same file it uploads. If it doesn't, stop and tell the user rather than pinning it anyway. Then pin that full SHA, because this workflow runs with your repository token, OIDC, and attestation permissions.

Tell the user where that pin stops. It freezes `release.yml`, not the actions `release.yml` calls. Those sit on mutable tags of their own (`actions/checkout@v4`, the CLI, the attest action) and resolve at run time under the same permissions, and a caller cannot pin them. Fork the workflow if that residual risk is unacceptable.

```yaml
# .github/workflows/release.yml — verified build on every version tag
name: Build and Release
on:
push:
tags: ["v*"]

permissions: # inherited by the called workflow, which cannot elevate them
contents: write # create the release, attach the WASM
id-token: write # sign the attestation
attestations: write # publish it

jobs:
release:
# the SHA you resolved and reviewed; keep the version in the comment
uses: stellar-expert/soroban-build-workflow/.github/workflows/release.yml@<commit-sha> # vX.Y.Z
with:
release_name: ${{ github.ref_name }}
home_domain: example.com # optional
package: my-contract # optional — omit to build the working directory
secrets:
release_token: ${{ secrets.GITHUB_TOKEN }} # created by GitHub, nothing to configure
```

Then deploy **the WASM attached to that release**, not a local rebuild — compilation environments vary, and a hash that differs by one byte matches no attestation.

The result shows up in [Stellar Lab's contract explorer](https://developers.stellar.org/docs/tools/lab/smart-contracts/contract-explorer#build-info) and on stellar.expert. Be precise with users about what it proves: a specific workflow run built this binary from this commit. It says nothing about whether that code is safe or was ever reviewed.

## Minimal test

```rust
Expand All @@ -211,7 +264,7 @@ Auth mocking, event assertions, fuzzing, fork tests, and CI setup: [testing.md](

## Before mainnet

Work through the checklists in [security.md](security.md) — authorization, reinitialization, arithmetic, storage TTLs, and cross-contract validation are the recurring failure modes.
Work through the checklists in [security.md](security.md) — authorization, reinitialization, arithmetic, storage TTLs, and cross-contract validation are the recurring failure modes. Ship the deploy through a [verified build](#verify-your-build) so the on-chain WASM hash traces back to a reviewable commit.

## Documentation

Expand Down
1 change: 1 addition & 0 deletions skills/smart-contracts/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ Any contract that takes a token address must assume it may be native XLM's SAC,
- [ ] Events emitted for auditable state changes (and error codes never renumbered)
- [ ] Upgrade path gated, tested (happy + failure), and replay-safe
- [ ] Emergency controls (pause) and incident runbook defined for value-bearing contracts
- [ ] Deployed WASM traceable to its source — SEP-55 build metadata plus a GitHub attestation from a public repo, deployed from the release artifact ([SKILL.md](SKILL.md#verify-your-build))

### Client-side

Expand Down
1 change: 1 addition & 0 deletions skills/standards/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ This file carries the SEP/CAP standards routing map. The other two live alongsid
- Frontend SEP-7 / SEP-10 flows → `../dapp/SKILL.md`
- CAPs for cryptography (BLS, BN254, Poseidon) → `../zk-proofs/SKILL.md`
- x402/MPP protocol context → `../agentic-payments/SKILL.md`
- SEP-55 verified builds for mainnet contracts → `../smart-contracts/SKILL.md`

---

Expand Down
Loading