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
15 changes: 15 additions & 0 deletions .github/workflows/dx-e2e-beta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ jobs:
journey: ${{ matrix.journey }}
github-token: ${{ secrets.GITHUB_TOKEN }}

swift-sdk:
name: beta (macos-15 · 11-swift-sdk)
needs: warmup
runs-on: macos-15
steps:
- uses: actions/checkout@v4
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.4'
- uses: ./.github/actions/dx-e2e-run
with:
channel: beta
journey: 11-swift-sdk
github-token: ${{ secrets.GITHUB_TOKEN }}

java-sdk:
name: beta (ubuntu-24.04 · 12-java-sdk)
needs: warmup
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/dx-e2e-stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,21 @@ jobs:
journey: ${{ matrix.journey }}
github-token: ${{ secrets.GITHUB_TOKEN }}

swift-sdk:
name: stable (macos-15 · 11-swift-sdk)
needs: warmup
runs-on: macos-15
steps:
- uses: actions/checkout@v4
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.4'
- uses: ./.github/actions/dx-e2e-run
with:
channel: stable
journey: 11-swift-sdk
github-token: ${{ secrets.GITHUB_TOKEN }}

java-sdk:
name: stable (ubuntu-24.04 · 12-java-sdk)
needs: warmup
Expand Down
21 changes: 21 additions & 0 deletions journeys/11-swift-sdk/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// swift-tools-version: 6.1

import PackageDescription

let package = Package(
name: "SwiftSDKJourney",
platforms: [.macOS(.v14)],
dependencies: [
.package(url: "https://github.com/tx3-lang/swift-sdk.git", exact: "0.15.0"),
.package(name: "DemoClient", path: "__GENERATED__"),
],
targets: [
.executableTarget(
name: "SwiftSDKJourney",
dependencies: [
.product(name: "Tx3SDK", package: "swift-sdk"),
.product(name: "DemoClient", package: "DemoClient"),
]
)
]
)
54 changes: 54 additions & 0 deletions journeys/11-swift-sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# 11-swift-sdk

The generated Swift client developer journey against the real assembled toolchain: `trix`
scaffolds a project, its built-in `swift-client` plugin delegates to the real `tx3c` template,
SwiftPM compiles that generated package inside a clean host consumer, and the executable drives
resolve → sign → submit → confirmed against the disposable local Dolos devnet.

- **Scope:** native macOS runtime journey. It requires Swift 6.1/Xcode 16.4, Git, and the normal
`trix`, `tx3c`, `dolos`, and `cshell` toolchain. No live-network secret is used.
- **Toolchain gate:** `#@ min-tx3c: 0.25.0`, the first tx3c release containing the built-in
`swift-client` template. Older tx3c releases skip before checking the Swift prerequisite. The
journey also fails explicitly
if trix is older than `0.28.0`, the first release registering `swift-client`. Stable and beta
manifests must supply tx3c `^0.25.0` and trix `^0.28.0` for CI to count as coverage.
- **Package pin:** the host package requires the public Swift SDK at exactly `0.15.0`
(published annotated tag `v0.15.0`). The generated
package's `from: 0.15.0` constraint therefore resolves to that exact version as well.
- **Fixtures:** `Package.swift` and `main.swift` model the host application. Generated files stay
under `.tx3/codegen/swift-client/demo/` and are never checked in.

For pre-publication validation only, `TX3_SWIFT_SDK_PATH=/path/to/reviewed/swift-sdk` makes the
journey clone that checkout into its disposable workdir, tag the clone as `0.15.0`, and configure a
SwiftPM mirror. This exercises the same exact-version package graph without mutating the source
checkout. Released-channel CI intentionally leaves the override unset and resolves the public tag.

## Phases

1. `trix init -y` and `trix codegen --plugin swift-client` assert built-in rendering, per-protocol
layout, the embedded TIR envelope, party setters, and the typed transaction method.
2. `swift build --configuration debug` compiles both the generated `DemoClient` library and an
independent executable consumer against the exact SDK package requirement.
3. `devnet.toml` funds the canonical CIP-1852 signer vector from
`sdk-spec/test-vectors/crypto/signer-vectors.json`; the consumer binds that vector's mnemonic
and address with trix's deterministic `bob` receiver. trix's `alice` uses an underived Icarus
root key and cannot be used with the SDK's CIP-1852 signer. The consumer resolves, signs, and
submits a transfer, then waits for a confirmed status from the local devnet. The exit trap
reaps only this journey's Dolos config path, including when devnet startup or the consumer fails.

## Verification

```sh
TX3_TRIX_PATH=/path/to/trix \
TX3_TX3C_PATH=/path/to/tx3c \
TX3_DOLOS_PATH=/path/to/dolos \
TX3_CSHELL_PATH=/path/to/cshell \
./run.sh --local --journey 11-swift-sdk

./run.sh --channel stable --journey 11-swift-sdk
./run.sh --channel beta --journey 11-swift-sdk
```

Both CI workflows select this journey on native `macos-15` with Xcode `16.4`. A missing Swift
compiler or unresolved SDK package fails the job. Failed runs preserve the consumer, generated
package, devnet files, and command logs through the harness's artifact upload.
5 changes: 5 additions & 0 deletions journeys/11-swift-sdk/devnet.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Canonical SDK CIP-1852 signer vector, funded only in this disposable devnet.
# Source: sdk-spec/test-vectors/crypto/signer-vectors.json (cardanoCip1852).
[[utxos]]
address = "addr_test1vq8ac7qqy0vtulyl7wntmsxc6wex80gvcyjy33qffrhm7ss9hjl0y"
value = 100000000000
109 changes: 109 additions & 0 deletions journeys/11-swift-sdk/journey.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
#
# Journey 11 — Swift SDK generated-client DX. See README.md for coverage.
# Run via e2e/run.sh, which provides $TRIX and an isolated working directory.
#
#@ min-tx3c: 0.25.0

source "${E2E_LIB:?E2E_LIB not set — run this journey via e2e/run.sh}"

JOURNEY_HOME="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"

journey_begin "11-swift-sdk" "generate a Swift client → compile a clean consumer → sign, submit, and confirm on devnet"

TRIX_VERSION="$("${TRIX}" --version | sed -n 's/^trix //p')"
version_ge "${TRIX_VERSION}" "0.28.0" \
|| die "journey 11 requires trix >= 0.28.0 with the built-in swift-client plugin"

command -v swift >/dev/null 2>&1 \
|| die "swift not found — journey 11 requires the approved macOS/Xcode runner"
command -v git >/dev/null 2>&1 || die "git not found — SwiftPM package resolution requires git"

# 1. Scaffold under the stable protocol name `demo`, then drive trix into the
# built-in tx3c swift-client template. The output is a standalone SwiftPM
# package, unlike the bare-module TypeScript output exercised by journey 07.
mkdir demo && cd demo
run_cmd "trix init -y — scaffold a new project" "${TRIX}" init -y
run_cmd "trix codegen --plugin swift-client — generate bindings" \
"${TRIX}" codegen --plugin swift-client
assert_output_contains "Added [[codegen]]" "plugin entry seeded into trix.toml"
assert_output_contains "Bindgen successful for 'demo'"
grep -qiF "Reading template from" "${LAST_OUTPUT_FILE}" \
&& die "built-in plugin downloaded templates instead of using tx3c's built-in template"
ok "built-in swift-client template rendered by tx3c, no template download"

GENERATED="${PWD}/.tx3/codegen/swift-client/demo"
assert_exists "${GENERATED}/Package.swift" "generated SwiftPM package manifest"
assert_exists "${GENERATED}/Sources/DemoClient/Client.swift" "generated typed client"
assert_exists "${GENERATED}/Sources/DemoClient/Types.swift" "generated parameter types"
[[ -e "${PWD}/.tx3/codegen/swift-client/Package.swift" ]] \
&& die "generated package leaked outside the per-protocol subdirectory"

run_cmd "read the generated client" cat "${GENERATED}/Sources/DemoClient/Client.swift"
assert_output_contains 'PROTOCOL_NAME = "demo"' "protocol identity embedded"
assert_output_contains "TRANSFER_TIR" "transfer TIR envelope embedded"
assert_output_contains "struct DemoClient" "typed Swift client emitted"
assert_output_contains "func withSender" "sender party setter emitted"
assert_output_contains "func transfer" "typed transfer builder emitted"

# 2. Compile a separate host package. Its direct exact SDK requirement pins the
# generated package's compatible `from: 0.15.0` constraint to precisely 0.15.0.
mkdir -p app/Sources/SwiftSDKJourney
sed "s|__GENERATED__|${GENERATED}|g" "${JOURNEY_HOME}/Package.swift" > app/Package.swift
cp "${JOURNEY_HOME}/main.swift" app/Sources/SwiftSDKJourney/main.swift

# Pre-publication runs may point at a reviewed SDK checkout without mutating it.
# Clone it inside the disposable journey workdir, add the release-equivalent
# tag there, and use SwiftPM's official mirror mechanism. Released-channel CI
# leaves this unset and therefore resolves the real public 0.15.0 tag.
if [[ -n "${TX3_SWIFT_SDK_PATH:-}" ]]; then
[[ -f "${TX3_SWIFT_SDK_PATH}/Package.swift" ]] \
|| die "TX3_SWIFT_SDK_PATH must name a Swift SDK checkout"
run_cmd "clone reviewed Swift SDK checkout for local package resolution" \
git clone --quiet "${TX3_SWIFT_SDK_PATH}" .tx3/swift-sdk-mirror
run_cmd "tag the disposable mirror as the release-equivalent 0.15.0" \
git -C .tx3/swift-sdk-mirror tag 0.15.0
run_cmd "configure the consumer to use the local SDK mirror" \
swift package --package-path app config set-mirror \
--original https://github.com/tx3-lang/swift-sdk.git \
--mirror "file://${PWD}/.tx3/swift-sdk-mirror"
fi

run_cmd "swift build --configuration debug — compile generated client and consumer" \
swift build --package-path app --configuration debug
assert_exists "app/.build/debug/SwiftSDKJourney" "host consumer compiled"

# 3. Fund the canonical CIP-1852 signer vector in this disposable devnet.
# trix's alice uses an underived Icarus root key, so it cannot be bound to
# CardanoSigner. These public SDK vector credentials are devnet-only fixtures.
cp "${JOURNEY_HOME}/devnet.toml" devnet.toml
ALICE="addr_test1vq8ac7qqy0vtulyl7wntmsxc6wex80gvcyjy33qffrhm7ss9hjl0y"
BOB="$("${TRIX}" identities bob address-testnet 2>/dev/null | grep '^addr' | head -n1)"
[[ -n "${BOB}" ]] || die "could not resolve bob's testnet address"
ALICE_MNEMONIC="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"

DEVNET_HOME="${PWD}/.tx3/dolos"
cleanup_devnet() {
# Install before startup and match only this journey's unique config path.
pkill -f "dolos.*${DEVNET_HOME}/dolos[.]toml daemon" >/dev/null 2>&1 || true
}
trap cleanup_devnet EXIT
run_cmd "trix devnet --background — start a local devnet" "${TRIX}" devnet --background
assert_output_contains "devnet started in background"
for _ in $(seq 1 30); do
(exec 3<>/dev/tcp/127.0.0.1/8164) 2>/dev/null && { exec 3>&- 3<&-; break; }
sleep 1
done
sleep 3

run_cmd "SwiftSDKJourney — resolve, sign, submit, and confirm a transfer" \
app/.build/debug/SwiftSDKJourney \
"http://127.0.0.1:8164" "${ALICE}" "${BOB}" "${ALICE_MNEMONIC}"
assert_output_contains '"hash"' "submitted transaction hash returned"
if grep -Eq '"status":"(confirmed|finalized)"' "${LAST_OUTPUT_FILE}"; then
ok "submitted transaction confirmed"
else
die "submitted transaction did not reach confirmed or finalized status"
fi

journey_end
30 changes: 30 additions & 0 deletions journeys/11-swift-sdk/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import DemoClient
import Foundation
import Tx3SDK

let arguments = CommandLine.arguments
guard arguments.count == 5, let endpoint = URL(string: arguments[1]) else {
FileHandle.standardError.write(
Data("usage: SwiftSDKJourney <trp-endpoint> <sender> <receiver> <sender-mnemonic>\n".utf8)
)
exit(2)
}

let sender = try Address(arguments[2])
let receiver = try Address(arguments[3])
let signer = try CardanoSigner(mnemonic: arguments[4], address: sender)

let client = DemoClient(
options: ClientOptions(endpoint: endpoint),
profile: .local
)
.withSender(.signer(signer))
.withReceiver(.address(receiver))

let resolved = try await client.transfer(TransferParams(quantity: 2_000_000)).resolve()
let submitted = try await resolved.sign().submit()
let status = try await submitted.waitForConfirmed(
PollConfig(attempts: 30, delay: .seconds(1))
)

print("{\"hash\":\"\(submitted.hash)\",\"status\":\"\(status.stage.rawValue)\"}")
Loading