Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
68a95ab
add settlement IDL
kaze-cow Jul 28, 2026
55f0562
generate js client with codama and add a basic test to make sure it w…
kaze-cow Jul 16, 2026
7e63017
add validation of the idl
kaze-cow Jul 29, 2026
b6f6b97
Merge branch 'main' into idl
kaze-cow Jul 29, 2026
adf17c5
fix lint
kaze-cow Jul 29, 2026
aab1aba
Merge branch 'idl' of github.com:cowprotocol/solana-programs into idl
kaze-cow Jul 29, 2026
f398816
Merge branch 'main' into idl
kaze-cow Jul 31, 2026
d976a07
Merge branch 'main' into idl
kaze-cow Jul 31, 2026
44e827e
Merge branch 'main' into idl
kaze-cow Aug 5, 2026
6c63eae
Merge remote-tracking branch 'origin/main' into idl
kaze-cow Aug 12, 2026
eb45059
sync idl and add a couple more tests to cover everything
kaze-cow Aug 12, 2026
6c02f9b
Merge branch 'idl' into kaze/sc-255-write-idl-and-generate-correspond…
kaze-cow Aug 12, 2026
6c7149a
fix createorder test fialures
kaze-cow Aug 12, 2026
541bcee
Update programs/settlement/tests/idl.rs
kaze-cow Aug 20, 2026
4d04660
Update programs/settlement/tests/idl.rs
kaze-cow Aug 20, 2026
bae4326
various fixes from feedback
kaze-cow Aug 20, 2026
4b8e58a
Merge branch 'idl' of github.com:cowprotocol/solana-programs into idl
kaze-cow Aug 20, 2026
b5b0090
refactor idl tests into a folder with relevant code isolated
kaze-cow Aug 20, 2026
3190d0e
switch to an enum to make it more clear what a type is in the idl
kaze-cow Aug 20, 2026
7a78d05
Merge remote-tracking branch 'origin/main' into idl
kaze-cow Aug 20, 2026
7005093
update to the latest on the idl side
kaze-cow Aug 20, 2026
ba596f8
add test verifying the idl version matches cargo pkg
kaze-cow Aug 20, 2026
f4cf043
improve confirm_idl_match params naming
kaze-cow Aug 20, 2026
1f13365
Merge branch 'main' into idl
kaze-cow Aug 24, 2026
af8cfd3
add more idl checks, when it was easy enough
kaze-cow Aug 24, 2026
f733e64
Merge branch 'idl' of github.com:cowprotocol/solana-programs into idl
kaze-cow Aug 24, 2026
0ae07e2
rethink how the testing structure works
kaze-cow Aug 24, 2026
b924e85
remove unnecessary comments and add test to confirm link stripping
kaze-cow Aug 31, 2026
0e15786
Update programs/settlement/idl/cow_settlement.json
kaze-cow Aug 31, 2026
dfaba66
Update programs/settlement/idl/cow_settlement.json
kaze-cow Aug 31, 2026
fa8c4d2
add a test for targetless links
kaze-cow Aug 31, 2026
f746b8f
Merge branch 'idl' of github.com:cowprotocol/solana-programs into idl
kaze-cow Aug 31, 2026
f4f065e
remove unnecessary comment from IDL
kaze-cow Aug 31, 2026
7012d1f
Merge remote-tracking branch 'origin/main' into idl
kaze-cow Aug 31, 2026
4ba1d33
explain variants better
kaze-cow Aug 31, 2026
c1e993b
fix: merge conflicts
limitofzero Aug 31, 2026
3cb4b50
Merge branch 'idl' into kaze/sc-255-write-idl-and-generate-correspond…
kaze-cow Sep 1, 2026
efe5ef1
Merge remote-tracking branch 'origin/main' into kaze/sc-255-write-idl…
kaze-cow Sep 1, 2026
ef978a8
test(idl-client): build order intents from the current OrderIntent shape
kaze-cow Sep 1, 2026
3acbf98
refactor(idl): ship the IDL with the generated client
kaze-cow Sep 1, 2026
49a649e
ci(idl-client): run the JS client tests in CI
kaze-cow Sep 1, 2026
9736f86
simplifies and final manual review
kaze-cow Sep 1, 2026
f9f996b
fixes from pre-review
kaze-cow Sep 1, 2026
54699c3
feat: add publish github flow
limitofzero Sep 1, 2026
0d79022
feat: prevent token leak + rename the package
limitofzero Sep 1, 2026
1208a2a
clean up lock file
kaze-cow Sep 2, 2026
85b4c2f
Update Justfile
kaze-cow Sep 2, 2026
be88336
misc fixes from review
kaze-cow Sep 2, 2026
fedde4e
Merge branch 'kaze/sc-255-write-idl-and-generate-corresponding-librar…
kaze-cow Sep 2, 2026
5434c20
update package manager and solana kit version
kaze-cow Sep 2, 2026
5855be9
final cleanups by removing unnecessary lines
kaze-cow Sep 2, 2026
5ca9842
Update programs/settlement/idl/package.json
kaze-cow Sep 2, 2026
c9d9cb6
add order pda test
kaze-cow Sep 2, 2026
1d5e00f
Merge branch 'kaze/sc-255-write-idl-and-generate-corresponding-librar…
kaze-cow Sep 2, 2026
e4af21b
feat: add automatic action trigger
limitofzero Sep 2, 2026
d0bbf8c
fix: resolve conflicts
limitofzero Sep 2, 2026
a010d89
feat: use Trusted publisher approach to publish packages
limitofzero Sep 2, 2026
f797e26
fix: use esbuild
limitofzero Sep 2, 2026
3e7b018
chore: use the same pined v7 sha for action/checkout
limitofzero Sep 2, 2026
a1438ee
feat: address review comments
limitofzero Sep 6, 2026
63743d5
chore: simplify output
limitofzero Sep 6, 2026
b51ce52
merge: fix conflicts
limitofzero Sep 7, 2026
588fddb
fix: add hooks export & add example of usage & replace relative paths…
limitofzero Sep 7, 2026
3f08089
Merge branch 'main' into feat/implement-npm-publish-flow
limitofzero Sep 7, 2026
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
109 changes: 109 additions & 0 deletions .github/workflows/auto-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
name: Auto-create release on version bump

# Watches pushes to main for a change to the workspace version in Cargo.toml
# (the dedicated "bump version" PR from README.md's release flow). When it
# changes, this automatically cuts the GitHub Release that publish-npm.yml
# listens for, so merging the bump PR is the only manual step left before a
# release goes out (npm publish itself still needs manual approval, see
# publish-npm.yml's npm-publish environment).
on:
push:
branches: [main]

permissions:
contents: write
actions: write # to dispatch publish-npm.yml, see the note on the "Create release" step

jobs:
auto-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0

- name: Detect version bump
id: detect
run: |
# All-zeros `before` shows up on branch creation/force-push; there's
# no meaningful "previous version" to diff against, so skip.
if [ "${{ github.event.before }}" = "0000000000000000000000000000000000000000" ]; then
echo "changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi

old_version=$(git show "${{ github.event.before }}:Cargo.toml" 2>/dev/null | grep -m1 '^version = ' | sed -E 's/version = "(.*)"/\1/') || true
new_version=$(grep -m1 '^version = ' Cargo.toml | sed -E 's/version = "(.*)"/\1/')

if [ -z "$new_version" ] || [ "$old_version" = "$new_version" ]; then
echo "changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi

# Fail before creating anything if the bump PR forgot the npm package.
# Catching this here means no release/tag ever gets created against
# this commit — checking only after publish-npm.yml runs would leave
# a broken, immutable tag pinned to a commit that can never actually
# publish (a follow-up commit fixing just package.json wouldn't
# re-trigger this workflow, since it only watches Cargo.toml).
npm_version=$(node -p 'require("./programs/settlement/idl/client/js/package.json").version')
if [ "$new_version" != "$npm_version" ]; then
echo "::error::Cargo version $new_version does not match npm version $npm_version in programs/settlement/idl/client/js/package.json. Bump both together before merging." >&2
exit 1
fi

echo "changed=true" >> "$GITHUB_OUTPUT"
echo "version=$new_version" >> "$GITHUB_OUTPUT"
Comment thread
kaze-cow marked this conversation as resolved.

- name: Check for existing release
id: check
if: steps.detect.outputs.changed == 'true'
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.detect.outputs.version }}
run: |
tag="v$VERSION"
if gh release view "$tag" >/dev/null 2>&1; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi

- name: Create release
if: steps.detect.outputs.changed == 'true' && steps.check.outputs.exists == 'false'
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.detect.outputs.version }}
run: |
tag="v$VERSION"
# --target pins the release to the exact commit this run is for, not
# whatever main's tip happens to be when this step runs — otherwise a
# commit landing on main mid-run could get tagged/published instead.
gh release create "$tag" \
--target "$GITHUB_SHA" \
--title "Alpha release, $tag" \
--generate-notes
Comment thread
kaze-cow marked this conversation as resolved.

# A separate, unconditional-on-"just created" step: if a previous run
# created the release but failed before dispatching (e.g. a transient
# API error), re-running this job must still retry the dispatch instead
# of short-circuiting on "release already exists".
- name: Dispatch publish workflow
if: steps.detect.outputs.changed == 'true'
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.detect.outputs.version }}
run: |
tag="v$VERSION"
# GitHub suppresses the `release` event when the resource that triggers
# it (this release) was itself created using GITHUB_TOKEN — otherwise
# this would recurse. That means publish-npm.yml's `release: published`
# trigger will NOT fire for a release created here.
# https://docs.github.com/en/actions/concepts/security/github_token
# So dispatch it explicitly instead of relying on that event.
#
# Dispatch against the tag itself, not `main`: the tag is immutable and
# points at $GITHUB_SHA above, whereas `main` can move between this line
# running and the dispatched run's checkout, which would silently build
# and (pending approval) publish a different, unreviewed commit.
gh workflow run publish-npm.yml --ref "$tag" -f tag="$tag"
12 changes: 7 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ jobs:

test-js-client:
runs-on: ubuntu-latest
# Corepack otherwise asks for confirmation before fetching pnpm, which
# would hang the job.
env:
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
Expand All @@ -80,13 +84,11 @@ jobs:

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"
node-version: "24" # Node ships corepack, which just build-js-client/test-js-client use

- name: Build JS client
run: just build-js-client
- name: Test JS client
# Corepack otherwise asks for confirmation before fetching pnpm, which
# would hang the job.
env:
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
run: just test-js-client

fmt-check:
Expand Down
146 changes: 146 additions & 0 deletions .github/workflows/publish-npm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
name: Publish npm package

on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: >-
Release tag to publish (e.g. v0.4.0). Set automatically when
auto-release.yml dispatches this workflow (GITHUB_TOKEN-created
releases don't fire the `release` event, so it can't rely on that
trigger — see https://docs.github.com/en/actions/concepts/security/github_token).
Leave empty for a manual build-only dry run (skips the tag/version check).
required: false

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read # to look up releases in the "Cargo and npm versions must match" step
# Corepack otherwise asks for confirmation before fetching pnpm, which
# would hang the job.
env:
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
outputs:
package-name: ${{ steps.pkg.outputs.name }}
package-version: ${{ steps.pkg.outputs.version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
# A manual dispatch could pass --ref main while also supplying an
# unrelated existing release's tag as input; without this, the tag
# would pass the version check below while the build actually ran
# against whatever `main` happens to be, not the released commit.
# Pinning to the tag input (when present) makes that impossible.
ref: ${{ github.event.inputs.tag || github.ref }}
persist-credentials: false

- uses: ./.github/actions/setup-solana
- uses: ./.github/actions/setup-just
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24" # matches ci.yml's test-js-client job

- name: Build JS client
run: just build-js-client
- name: Test JS client
run: just test-js-client
Comment thread
kaze-cow marked this conversation as resolved.

- name: Read package metadata
id: pkg
working-directory: programs/settlement/idl/client/js
run: |
echo "name=$(node -p 'require("./package.json").name')" >> "$GITHUB_OUTPUT"
echo "version=$(node -p 'require("./package.json").version')" >> "$GITHUB_OUTPUT"

- name: Release tag, Cargo.toml, and package.json versions must all match
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag != '')
working-directory: programs/settlement/idl/client/js
env:
GH_TOKEN: ${{ github.token }}
run: |
# Real `release` events carry the tag as the ref; a dispatch from
# auto-release.yml passes it as an explicit input instead. That
# input is free text from whoever ran the dispatch, so unlike the
# `release` case it isn't proof a real release exists — confirm one
# does, rather than only checking it's self-consistent otherwise.
if [ "${{ github.event_name }}" = "release" ]; then
raw_tag="$GITHUB_REF_NAME"
else
raw_tag="${{ github.event.inputs.tag }}"
if ! gh release view "$raw_tag" >/dev/null 2>&1; then
echo "::error::No release named $raw_tag exists." >&2
exit 1
fi
fi

# Existing tags (v0.2, v0.3) are major.minor only, while npm/Cargo
# need full semver (0.3.0), so pad any missing patch component.
tag="${raw_tag#v}"
IFS='.' read -r major minor patch <<< "$tag"
normalized_tag="${major}.${minor:-0}.${patch:-0}"

cargo_version=$(grep -m1 '^version = ' ../../../../../Cargo.toml | sed -E 's/version = "(.*)"/\1/')
pkg_version="${{ steps.pkg.outputs.version }}"

if [ "$normalized_tag" != "$cargo_version" ] || [ "$cargo_version" != "$pkg_version" ]; then
echo "::error::Version mismatch — release tag $raw_tag (normalized $normalized_tag), Cargo.toml $cargo_version, package.json $pkg_version must all match." >&2
exit 1
fi
Comment on lines +66 to +93

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.

can we simplify this? like maybe read the version from Cargo.toml instead to see that it matches.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done, now reads the version from Cargo.toml directly instead of parsing the git tag string.

one thing I had to add back in: the check still also compares against the release tag itself, not just Cargo.toml vs package.json. First pass dropped that and I found it, since a release tagged v0.4.0 could otherwise pass with cargo.toml and package.json both still at 0.3.0, publishing the wrong version under the wrong tag. So now all three (tag, cargo.toml, package.json) have to agree.


- name: Write publish summary
working-directory: programs/settlement/idl/client/js
run: ./scripts/publish-summary.sh "${{ github.event.release.tag_name || github.event.inputs.tag || github.ref_name }}" >> "$GITHUB_STEP_SUMMARY"

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: npm-package
path: |
programs/settlement/idl/client/js/dist
programs/settlement/idl/client/js/package.json
programs/settlement/idl/client/js/README.md

publish:
needs: build
# A workflow_dispatch with an empty `tag` is documented as a build-only dry
# run (skips the version check above) — it must not be able to reach an
# actual `npm publish` just because someone approves the environment gate.
if: github.event_name == 'release' || github.event.inputs.tag != ''
runs-on: ubuntu-latest
# See README.md's "Publishing the Node.js client" section for what an
# approver here should check.
environment: npm-publish
permissions:
id-token: write # for npm provenance
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: npm-package
path: package

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"
registry-url: https://registry.npmjs.org

# Trusted Publishing (OIDC) needs npm >= 11.5.1; pinned since the bundled
# version can be older even on Node 24.
- name: Upgrade npm for Trusted Publishing
run: npm install -g npm@~11.10.0 # pinned like cow-sdk's release workflow, see https://github.com/npm/cli/issues/9151

- name: Publish ${{ needs.build.outputs.package-name }}@${{ needs.build.outputs.package-version }}
working-directory: package
# NODE_AUTH_TOKEN is only a bootstrap fallback for this package's very
# first publish, before a Trusted Publisher can be configured for it
# (see README.md's "Publishing the Node.js client"). `npm publish`
# always tries OIDC first regardless, so this is safe to leave set
# even after NPM_TOKEN is deleted (it just resolves to an empty string).
# --ignore-scripts: publish exactly the artifact reviewed in `build`,
# with no lifecycle script able to run and alter it at this point.
run: npm publish --provenance --access public --ignore-scripts
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ proptest-regressions/
.cargo-root/
node_modules/
generated/
dist/
.idea/

# Make sure no Solana key is uploaded by accident.
*.json
!bench-report.json
!programs/settlement/idl/**/*.json
!programs/settlement/idl/**/*.json
6 changes: 6 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ doc *args:
doc-dev *args:
cargo doc --workspace --no-deps --all-features --document-private-items --config 'build.rustdocflags=["--deny=warnings"]' {{ args }}

# Build the publishable TS/JS client package (bundles the Codama-generated code plus hand-written wrappers).
[working-directory: 'programs/settlement/idl/client/js']
@build-js-client: generate-js-client
corepack pnpm install --frozen-lockfile
corepack pnpm run build

# Build the settlement program using solana-verify's reproducible Docker build.
# Installs solana-verify via cargo if not already present (same as CI).
build-verified:
Expand Down
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ Then, all packages can published in one go:
cargo publish
```

### Publishing the Node.js client

The TS/JS client (`@cowprotocol/solana-settlement-client`, generated from `programs/settlement/idl/cow_settlement.json` via Codama) is published automatically by [`publish-npm.yml`](.github/workflows/publish-npm.yml) whenever a GitHub release is cut — its version must already match the release tag (see [Bumping the crate version](#bumping-the-crate-version), which bumps it alongside the crates). The release itself is also created automatically, by [`auto-release.yml`](.github/workflows/auto-release.yml), as soon as a version-bump PR merges into `main` — see the [Breaking change](#breaking-change) and [Patch update](#patch-update) flows above. Merging the bump PR is the only manual step left before a release goes out; npm publishing still needs manual approval (below).

Publishing requires manual approval: `publish-npm.yml`'s publish step runs under a [GitHub Environment](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment) named `npm-publish`, configured in repo Settings → Environments with required reviewers. Before approving, check the job summary the workflow posts: it lists the exact tarball contents about to be published and a dependency diff against the previously published version. Approve only if both look as expected for the changes in this release.

Authentication to npm uses [Trusted Publishing](https://docs.npmjs.com/trusted-publishers) — no stored npm token. One-time setup after the package's *first* publish (it can't be configured before the package exists): on npmjs.com, add a Trusted Publisher for this repo, `publish-npm.yml`, and the `npm-publish` environment, then delete the `NPM_TOKEN` secret.

### Devnet example

```sh
Expand Down Expand Up @@ -155,22 +163,21 @@ You can use the settle CLI for a smoke test of the programs after a release. See
- [Publish the IDL](#publishing-the-idl).
- Authorize all [currently existing solver](https://app.notion.com/p/cownation/Solvers-for-Solana-Dev-Contracts-3ca8da5f04ca80968642e85640178cbd) using the solver CLI (`cow solver add --help`).
- Make sure the package installs without errors: run `cargo install --path /mnt/lima-solana/repos/solana-programs/solana-program-workbench/test-cli --locked` (it depends on all other packages).
- Create a PR with the changes and wait for approval.
- Create a PR with the changes and wait for approval, then merge it. Merging automatically creates a GitHub release (tag `v$VERSION`, e.g. `v0.42.0`) via [`auto-release.yml`](.github/workflows/auto-release.yml), which in turn triggers the npm package publish workflow — see [Publishing the npm package](#publishing-the-npm-package).
- [Publish the cargo packages](#publishing-the-cargo-packages).
- Create a [new GitHub release](https://github.com/cowprotocol/solana-programs/releases/new); in doing so, create a new tag like `v0.42`; title "Alpha release, v0.42".

### Patch update

- Check out the `main` branch. Make sure there are no local changes (`git status --porcelain` is empty).
- [Bump the crate version](#bumping-the-crate-version) by a patch version.
- Commit the code changes resulting from the changes above.
- Create a PR with the changes and wait for approval.
- Create a PR with the changes and wait for approval, then merge it. Merging automatically creates a GitHub release (tag `v$VERSION`, e.g. `v0.42.1`) via [`auto-release.yml`](.github/workflows/auto-release.yml), which in turn triggers the npm package publish workflow — see [Publishing the npm package](#publishing-the-npm-package).
- [Update the programs](#how-to-deploy). The deployer keypair and the program keypair are in 1password (stored respectively under "Solana Deployer" and "Settlement account by version").
- [Publish the cargo packages](#publishing-the-cargo-packages).

### Bumping the crate version

You need to update Cargo's toml and lock file.
You need to update Cargo's toml and lock file, and the npm package's version (kept in lockstep so a release tag maps to one version everywhere).
Here is a list of commands to help bumping all relevant strings:

```sh
Expand All @@ -179,6 +186,7 @@ perl -i -pe '
s/^version = ".*"/version = "$ENV{VERSION}"/;
s/(path = "[^"]*", version = )"[^"]*"/$1"$ENV{VERSION}"/;
' ./Cargo.toml
perl -i -pe 's/^(\s*"version": )".*"/$1"$ENV{VERSION}"/' ./programs/settlement/idl/client/js/package.json
just build
```

Expand Down
1 change: 1 addition & 0 deletions programs/settlement/idl/client/js/.prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
src/generated/
pnpm-lock.yaml
dist/
Loading