Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4a1a8c4
separate viem + ethers, add viem extend()
bjfresh Sep 15, 2026
3cb448b
viem + ethers separation + tests
bjfresh Sep 17, 2026
d53b6e9
add new checkProtocolDeployment method
bjfresh Sep 17, 2026
740db7d
support pinning the anvil fork block, and document the tradeoffs
bjfresh Sep 29, 2026
730f662
make the examples usable by anyone, not just the NFT's owner
bjfresh Sep 29, 2026
4f24fd4
correct the contributor setup instructions
bjfresh Sep 29, 2026
f3c62bf
support cross-chain execution through @tokenbound/ethers
bjfresh Sep 29, 2026
4ecaf8e
typecheck the test trees
bjfresh Sep 29, 2026
a7489c5
remove deployerUrl from response
bjfresh Sep 30, 2026
ed67f07
remove getEthersVersion, re-implement getSDKVersion for method parity
bjfresh Sep 30, 2026
927084f
fix stale executeTo dependency in the examples' execute callback
bjfresh Sep 30, 2026
be02d82
fix biome ci failures
bjfresh Sep 30, 2026
1543da3
point @tokenbound/ethers types at the emitted declarations
bjfresh Sep 30, 2026
79d4afb
fall back to the public RPC when the fork endpoint is empty, not just…
bjfresh Sep 30, 2026
ade807d
await receipts before reading balances; fall back when fork endpoint …
bjfresh Sep 30, 2026
b020b3e
await receipts before reading balances in the minting tests
bjfresh Oct 1, 2026
a0a91c0
read the mint nonce from the chain, not ethers' cache
bjfresh Oct 1, 2026
1ed960b
use a node-managed signer for ethers v6 in tests
bjfresh Oct 1, 2026
6addab3
strip nextNonce() calls
bjfresh Oct 1, 2026
c2b191d
drop redundant tx.wait() calls that can stall on ethers v6
bjfresh Oct 1, 2026
867b437
poll for receipts instead of relying on viem's block watcher
bjfresh Oct 1, 2026
ac4c5eb
Replace legacy @viem/anvil with prool
bjfresh Oct 14, 2026
58a72fa
bump viem to 2.57.7 across the workspace
bjfresh Oct 14, 2026
94a55d0
set window.tokenboundSDK from the ethers client; fix implementation l…
bjfresh Oct 14, 2026
048b45c
ignore Vite's leftover config temp files
bjfresh Oct 14, 2026
9b93b44
Add end-to-end cross-chain test
bjfresh Oct 19, 2026
8f7c246
make viem a peer dependency and build with tsdown
bjfresh Oct 20, 2026
342e7c7
publish the released tag, after tests, with provenance
bjfresh Oct 20, 2026
8baa89a
add TODO.md for remaining release work and open questions
bjfresh Oct 20, 2026
431e5b2
add end-to-end cross-chain test for @tokenbound/ethers
bjfresh Oct 20, 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
18 changes: 9 additions & 9 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["vite-wagmi"]
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["vite-wagmi"]
}
59 changes: 59 additions & 0 deletions .changeset/viem-first-sdk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
"@tokenbound/sdk": major
"@tokenbound/ethers": minor
---

Make `@tokenbound/sdk` viem-first and move ethers v5/v6 support into a new
`@tokenbound/ethers` package.

**`@tokenbound/sdk`**

- Removed the `signer` option and all ethers-specific types, utilities and
runtime branches. The SDK no longer has ethers anywhere in its dependency
graph.
- Added an idiomatic viem client decorator: `.extend(tokenboundActions())`
exposes the API under `client.tokenbound.*`, with write actions typed only for
clients that carry an account.
- Extracted a library-independent protocol layer (`@tokenbound/sdk/protocol`)
that operates on `Address`/`Hex`/`bigint`/chain ids/ABIs, shared by the viem
core and `@tokenbound/ethers`.
- **Breaking:** `chain` is now required; passing only `chainId` is no longer
supported. The internal `chainIdToChain` lookup eagerly imported 17 chains
from `viem/chains` into every consumer bundle.
- **Breaking:** the constructor `chainId` option is removed; the chain id now
always comes from `chain`. Per-method `chainId` params (cross-chain execution,
deriving an account for another chain) are unchanged.
- **Breaking:** `signMessage` accepts only viem's `SignableMessage`.
- Added `./viem` and `./protocol` subpath exports and `"sideEffects": false`.
- Relocated the ERC-6551 ABIs out of the test tree, so generated test fixtures
(Zora, WETH) no longer end up in the published bundle.
- Bundle: 251.7 kB → 15.9 kB (74.5 kB → 3.8 kB gzipped). Published tarball:
226.2 kB → ~80 kB. Bundle analysis is now opt-in (`ANALYZE=true pnpm build`)
and no longer writes a 668 kB `stats.html` into `dist/`.

**`@tokenbound/ethers`** (new)

- Ethers v5 and v6 compatibility, detected automatically at construction.
- Same public API as the SDK's `TokenboundClient`, so ethers users migrate by
changing the import and passing a `chain`.
- Reuses all ERC-6551 protocol logic from `@tokenbound/sdk`; no duplication.

**Examples**

- Updated the viem example to the `.extend(tokenboundActions())` API, and
repointed the ethers examples at `@tokenbound/ethers`.
- Bumped the examples to Vite 5. Vite 4 bundles esbuild 0.18, which cannot parse
the import attributes (`with { type: "json" }`) used by `@base-org/account` via
wagmi's connectors, so `vite dev` failed to start.

**Packaging / tooling**

- Added `description`, `keywords`, `repository`, `homepage` and `bugs` to both
packages — none were previously set, so npm had no linked source or issues.
- Dropped unused React/testing-library devDependencies from `@tokenbound/sdk`
(no React components or DOM tests remain) and removed the dead jest-dom test
setup file.
- Switched `moduleResolution` from the deprecated `Node` (node10, removed in
TypeScript 7) to `Bundler`, which also resolves the package `exports` map.

See `MIGRATION.md` for full migration notes.
95 changes: 95 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
build-and-validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10

- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Lint and format check
run: pnpm biome ci .

- name: Typecheck @tokenbound/sdk
run: pnpm --filter @tokenbound/sdk exec tsc --noEmit

- name: Build @tokenbound/sdk
run: pnpm --filter @tokenbound/sdk build

- name: Typecheck @tokenbound/ethers
run: pnpm --filter @tokenbound/ethers exec tsc --noEmit

- name: Build @tokenbound/ethers
run: pnpm --filter @tokenbound/ethers build

# Fails if tests, examples, stats.html, coverage or raw sources would be
# published, and reports packed/unpacked sizes so regressions are visible.
- name: Validate published package contents
run: node scripts/validate-package-contents.mjs

- name: Assert @tokenbound/sdk pulls in no ethers
run: |
if pnpm --filter @tokenbound/sdk why ethers 2>/dev/null | grep -q 'ethers@'; then
echo "::error::@tokenbound/sdk must not depend on ethers"
pnpm --filter @tokenbound/sdk why ethers
exit 1
fi
echo "✓ @tokenbound/sdk has no ethers in its dependency graph"

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10

- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: pnpm

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build @tokenbound/sdk
run: pnpm --filter @tokenbound/sdk build

# Deterministic protocol + viem extension tests, including the
# type-level assertions (expectTypeOf) for the .extend() surface.
- name: Unit and type tests (@tokenbound/sdk)
run: pnpm --filter @tokenbound/sdk exec vitest --watch=false --typecheck run src/test/unit/

# ethers v5 and v6 are exercised independently, each against its own fork,
# across both the ERC-6551 V2 (legacy) and V3 deployments. The suites run
# as separate vitest processes because ethers caches nonces per provider.
- name: Ethers v5/v6 tests (@tokenbound/ethers)
run: pnpm --filter @tokenbound/ethers test
env:
VITE_ANVIL_MAINNET_FORK_ENDPOINT: ${{ secrets.VITE_ANVIL_MAINNET_FORK_ENDPOINT }}

# Anvil-backed viem integration suite.
- name: Integration tests (@tokenbound/sdk)
run: pnpm --filter @tokenbound/sdk test
env:
VITE_ANVIL_MAINNET_FORK_ENDPOINT: ${{ secrets.VITE_ANVIL_MAINNET_FORK_ENDPOINT }}
68 changes: 49 additions & 19 deletions .github/workflows/on-create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,70 @@ on:
release:
types: [published]

permissions:
contents: read
# Lets npm sign a provenance attestation tying each version to this workflow
# run and commit.
id-token: write

jobs:
publish:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18]
steps:
# A release event checks out the release's tag, so what is published is
# exactly what was released, not whatever main has moved on to since.
- name: Clone repository
uses: actions/checkout@v4
with:
ref: 'main' # Ensure we're on the main branch
fetch-depth: 0

- name: Require the release tag to be on main
run: |
if ! git merge-base --is-ancestor HEAD origin/main; then
echo "::error::Release tag ${{ github.ref_name }} is not on main"
exit 1
fi

- name: Set up PNPM
uses: pnpm/action-setup@v2
uses: pnpm/action-setup@v4
with:
version: 8
version: 10

- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
node-version-file: .nvmrc
registry-url: 'https://registry.npmjs.org'
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Lint and format check
run: pnpm biome ci .

# Navigate to the specific package directory within the monorepo
- name: Change to package directory
run: cd packages/sdk
# @tokenbound/ethers depends on @tokenbound/sdk, so the SDK builds first.
- name: Build packages 🔧
run: |
pnpm --filter @tokenbound/sdk build
pnpm --filter @tokenbound/ethers build

# - name: Install dependencies
# run: pnpm install
- name: Clean install and build 🔧
run: cd packages/sdk && pnpm i --force --frozen-lockfile && pnpm build
# run: pnpm ci && pnpm build // pnpm ci not yet implemented: https://github.com/pnpm/pnpm/issues/6100
# The fork-backed suites already ran in CI on main; these are the
# deterministic tests, which need no RPC endpoint or anvil.
- name: Unit and type tests
run: pnpm --filter @tokenbound/sdk exec vitest --watch=false --typecheck run src/test/unit/

- name: Validate published package contents
run: node scripts/validate-package-contents.mjs

- name: Publish @tokenbound/sdk 📦
run: pnpm --filter @tokenbound/sdk publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
NPM_CONFIG_PROVENANCE: true

- name: Publish package on NPM 📦
run: cd packages/sdk && pnpm publish --publish-branch main --access public
- name: Publish @tokenbound/ethers 📦
run: pnpm --filter @tokenbound/ethers publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
NPM_CONFIG_PROVENANCE: true
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ dist-ssr
/cache
/out
/.vscode

# Vite leaves these behind when a build is interrupted mid-config-load
*.timestamp-*.mjs
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
Loading
Loading