Skip to content
Open
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
2 changes: 1 addition & 1 deletion .changeset/standalone-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"@tevm/example-vite": patch
"client": patch
"contracts": patch
"svelte-ethers": patch
"@tevm/example-svelte-ethers": patch
---

Extract the Tevm examples into a standalone integration workspace that consumes published packages.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ jobs:
node-version-file: .nvmrc
cache: pnpm

# MUD 2.2.23 calls `forge config --json`, which Foundry 1.31 removed.
- uses: foundry-rs/foundry-toolchain@v1
with:
version: v1.5.1

- run: pnpm install --frozen-lockfile
- run: pnpm lint
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@ jobs:
deploy:
name: Deploy to Vercel
needs: build
# Vercel's Git integration deploys examples.tevm.sh on every push to main.
# This job only runs when the repo is configured for CLI deploys instead
# (VERCEL_TOKEN present), so the two mechanisms never race.
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 20
Expand All @@ -79,9 +76,12 @@ jobs:
cache: pnpm

- name: Deploy
if: ${{ env.VERCEL_TOKEN != '' }}
working-directory: docs
run: |
if [[ -z "$VERCEL_TOKEN" || -z "$VERCEL_ORG_ID" || -z "$VERCEL_PROJECT_ID" ]]; then
echo "::error::VERCEL_TOKEN, VERCEL_ORG_ID, and VERCEL_PROJECT_ID are required for the production docs deployment."
exit 1
fi
npx --yes vercel@latest pull --yes --environment=production --token="$VERCEL_TOKEN"
npx --yes vercel@latest build --prod --token="$VERCEL_TOKEN"
npx --yes vercel@latest deploy --prebuilt --prod --token="$VERCEL_TOKEN"
10 changes: 10 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,18 @@ jobs:
cache: pnpm
registry-url: https://registry.npmjs.org

# MUD 2.2.23 calls `forge config --json`, which Foundry 1.31 removed.
- uses: foundry-rs/foundry-toolchain@v1
with:
version: v1.5.1

- run: npm install --global npm@latest
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm typecheck
- run: pnpm build
- run: pnpm test
- run: pnpm docs:build

- uses: changesets/action@v1
with:
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ documentation remains at [tevm.sh](https://tevm.sh).

- Node.js 24 (the exact tested version is in `.nvmrc`)
- pnpm 9
- Foundry for the Solidity-backed Vite and MUD examples
- Foundry 1.5.1 for the Solidity-backed Vite and MUD examples
- Bun only when running the Bun example interactively

## Install
Expand All @@ -36,7 +36,7 @@ The apps live in [`apps/`](./apps/):
## Documentation site

The site published at [examples.tevm.sh](https://examples.tevm.sh) lives in [`docs/`](./docs) and is built with
[Vocs](https://vocs.dev) 2.7.0.
[Vocs](https://vocs.dev) 2.7.2.

```sh
pnpm docs:dev
Expand All @@ -59,15 +59,15 @@ and `RPC_URL_MAINNET` for the esbuild example.
## Published-package policy

Cross-repository Tevm dependencies use caret ranges resolved from npm. The `tevm` meta-package is declared as a
runtime peer where an example imports the node directly, and is installed as a development dependency for local
validation. The only workspace dependency is the MUD client's link to its contracts package, because both live in
this repository.
runtime peer at `^1.0.0-next.149` where an example imports the node directly, and is installed as a development
dependency for local validation. The only workspace dependency is the MUD client's link to its contracts package,
because both live in this repository.

## Releases

Changesets records release intent. Merges to `main` run the release workflow with npm provenance. The example
workspaces are private applications, so they are versioned and tagged but are not published as npm libraries unless
their `private` flags are deliberately removed.
Changesets records release intent. Merges to `main` run the full lint, typecheck, build, test, and docs gates before
the release action publishes the six `@tevm/example-*` packages with npm provenance. The docs and MUD's internal
`client` and `contracts` workspaces remain private.

## License

Expand Down
11 changes: 9 additions & 2 deletions apps/bun/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
{
"name": "@tevm/example-bun",
"version": "0.0.1",
"private": true,
"private": false,
"description": "Bun example using Tevm Solidity imports and a forked memory client",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/evmts/tevm-examples.git",
"directory": "apps/bun"
},
"contributors": [
"Will Cory <willcory10@gmail.com>"
],
Expand Down Expand Up @@ -30,7 +37,7 @@
"vitest": "^4.1.6"
},
"peerDependencies": {
"tevm": ">=1.0.0"
"tevm": "^1.0.0-next.149"
},
"publishConfig": {
"access": "public",
Expand Down
5 changes: 5 additions & 0 deletions apps/bun/readContract.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { ERC20 } from 'tevm/contract'
import { expect, test } from 'vitest'
import { readContract } from './readContract'

test('uses the ERC20 ABI exported by the published tevm package', () => {
expect(ERC20.abi.some((item) => item.type === 'function' && item.name === 'balanceOf')).toBe(true)
})

// This integration test needs a caller-provided fork endpoint; CI has no RPC credentials.
const testWithRpc = process.env.RPC_URL ? test : test.skip

Expand Down
9 changes: 8 additions & 1 deletion apps/esbuild/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
{
"name": "@tevm/example-esbuild",
"version": "0.0.1",
"private": true,
"private": false,
"description": "esbuild example using Tevm Solidity imports and viem",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/evmts/tevm-examples.git",
"directory": "apps/esbuild"
},
"contributors": [
"Will Cory <willcory10@gmail.com>"
],
Expand Down
9 changes: 9 additions & 0 deletions apps/esbuild/src/ownerOf.spec.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { describe, expect, it } from 'vitest'
import { getAddress } from 'viem'
import { addresses } from './addresses.js'
import { rpcUrls } from './constants.js'
import { mainnet } from 'viem/chains'

// This integration test intentionally runs only with an explicit mainnet RPC endpoint.
const itWithRpc = process.env.RPC_URL_MAINNET ? it : it.skip

describe('ownerOf', () => {
it('has a valid mainnet deployment and RPC URL', () => {
expect(getAddress(addresses[mainnet.id])).toBe(addresses[mainnet.id])
expect(() => new URL(rpcUrls[mainnet.id])).not.toThrow()
})

itWithRpc('should work', async () => {
const { ownerOf } = await import('./ownerOf.js')
await expect(ownerOf()).resolves.toBe('0x1a1E021A302C237453D3D45c7B82B19cEEB7E2e6')
Expand Down
2 changes: 1 addition & 1 deletion apps/esbuild/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
"incremental": true,
"types": ["bun-types"]
},
"include": ["**/*.ts", "**/*.tsx"],
"include": ["**/*.ts", "**/*.tsx", "**/*.js"],
"exclude": ["node_modules"]
}
9 changes: 8 additions & 1 deletion apps/mud/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
{
"name": "@tevm/example-mud",
"version": "0.0.0",
"private": true,
"private": false,
"description": "MUD application example for Tevm",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/evmts/tevm-examples.git",
"directory": "apps/mud"
},
"scripts": {
"build": "pnpm --filter contracts run build && pnpm --filter client run build",
"dev": "mprocs",
Expand Down
34 changes: 18 additions & 16 deletions apps/next/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# next-example
# Tevm Next.js example

Typescript build is currently broke (though app still runs) because this example project got deprioritized March 16th. We will update this example with working build, new features, and tevm bundler support in future.

Reach out if you are interested in contributing
A Next.js App Router application for inspecting contracts and simulating calls against a forked chain with a Tevm
memory client. The app is part of the
[`evmts/tevm-examples`](https://github.com/evmts/tevm-examples) integration workspace and is built in CI against
published Tevm packages.

## Table of contents

Expand Down Expand Up @@ -118,26 +119,27 @@ The minimal steps to get started are:

1. Clone the repository and navigate to this directory
```bash
git clone git@github.com:evmts/tevm-monorepo.git && cd tevm-monorepo/examples/next
git clone https://github.com/evmts/tevm-examples.git
cd tevm-examples/apps/next
```
2. Install the dependencies (preferably with [pnpm](https://pnpm.io))
```bash
pnpm install
```
3. Copy the `.env.local.example` file to `.env.local` and fill in the required environment variables
3. Create `.env.local` and add an Alchemy key for fork RPC calls. Explorer keys are optional, but improve ABI lookup
reliability for their corresponding chains.
```bash
cp .env.local.example .env.local
# Then edit .env.local
# ALCHEMY_API_KEY
# ETHERSCAN_API_KEY
# ARBISCAN_API_KEY
# BASESCAN_API_KEY
# OPTIMISTIC_ETHERSCAN_API_KEY
# POLYGONSCAN_API_KEY
# ZORA_SUPERSCAN_API_KEY
ALCHEMY_API_KEY=...
ETHERSCAN_API_KEY=...
ARBISCAN_API_KEY=...
BASESCAN_API_KEY=...
OPTIMISTIC_ETHERSCAN_API_KEY=...
POLYGONSCAN_API_KEY=...
```

We're using Alchemy for better modularity [when creating providers](./src/lib/constants/providers.ts#L66) and [Tevm clients](./src/lib/tevm.ts#L322), but you can replace it with any other provider, and update the way urls are created in the two aforementioned files.
The example uses Alchemy when [creating providers](./src/lib/constants/providers.ts) and
[Tevm clients](./src/lib/store/use-provider.ts). You can replace it with another provider by updating the RPC URLs
in those files.

4. Run the development server

Expand Down
13 changes: 10 additions & 3 deletions apps/next/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
{
"name": "@tevm/example-next",
"version": "0.0.1",
"private": true,
"private": false,
"description": "Next.js App Router example using a Tevm memory client",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/evmts/tevm-examples.git",
"directory": "apps/next"
},
"contributors": [
"Will Cory <willcory10@gmail.com>",
"0xpolarzero"
Expand All @@ -14,7 +21,7 @@
"format": "prettier --write \"**/*.{ts,tsx,mdx}\" --cache",
"format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache",
"start": "next start",
"test": "vitest run --passWithNoTests",
"test": "vitest run",
"typecheck": "tsc --noEmit"
},
"dependencies": {
Expand Down Expand Up @@ -66,7 +73,7 @@
"vitest": "^4.1.6"
},
"peerDependencies": {
"tevm": ">=1.0.0"
"tevm": "^1.0.0-next.149"
},
"publishConfig": {
"access": "public",
Expand Down
2 changes: 1 addition & 1 deletion apps/next/src/lib/constants/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Chain } from '@/lib/types/providers';
// TODO Create client here when top-level await is solved

export const ALCHEMY_API_KEY =
process.env.ALCHEMY_API_KEY || '_gg7wSSi0KMBsdKnGVfHDueq6xMB9EkC';
process.env.ALCHEMY_API_KEY ?? '';

// Chains that don't need an API key
export const STANDALONE_RPC_CHAINS = [
Expand Down
3 changes: 2 additions & 1 deletion apps/next/src/lib/constants/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const websiteUrl = 'https://tevm.sh';
* @dev This will be exported again from the layout file.
*/
export const METADATA_BASE: Metadata = {
metadataBase: new URL(websiteUrl),
title: title,
description: description,
applicationName: title,
Expand All @@ -26,7 +27,7 @@ export const METADATA_BASE: Metadata = {
siteName: title,
title: title,
description: description,
images: 'og-image.png',
images: '/og-image.png',
},
};

Expand Down
27 changes: 27 additions & 0 deletions apps/next/src/lib/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { describe, expect, it } from 'vitest';

import { formatInputValue } from './utils';

describe(formatInputValue.name, () => {
it('formats Solidity scalar values', () => {
expect(formatInputValue('uint256', '42')).toBe(BigInt(42));
expect(formatInputValue('bool', 'true')).toBe(true);
expect(formatInputValue('bytes32', '0x1234')).toBe('0x1234');
expect(
formatInputValue('address', '0x0000000000000000000000000000000000000000'),
).toBe('0x0000000000000000000000000000000000000000');
});

it('formats comma-separated Solidity arrays', () => {
expect(formatInputValue('uint256[]', '1,2,3')).toEqual([
BigInt(1),
BigInt(2),
BigInt(3),
]);
});

it('rejects invalid booleans and bytes', () => {
expect(() => formatInputValue('bool', 'yes')).toThrow('Invalid boolean');
expect(() => formatInputValue('bytes', 'not-hex')).toThrow('Invalid bytes');
});
});
1 change: 1 addition & 0 deletions apps/svelte-ethers/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_RPC_URL=https://ethereum-rpc.publicnode.com
2 changes: 1 addition & 1 deletion apps/svelte-ethers/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# svelte-ethers
# @tevm/example-svelte-ethers

## 0.1.3

Expand Down
14 changes: 11 additions & 3 deletions apps/svelte-ethers/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
{
"name": "svelte-ethers",
"name": "@tevm/example-svelte-ethers",
"version": "0.1.3",
"private": true,
"private": false,
"description": "SvelteKit example using the Tevm Ethers adapter and Solidity imports",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/evmts/tevm-examples.git",
"directory": "apps/svelte-ethers"
},
"type": "module",
"scripts": {
"build": "vite build",
Expand All @@ -19,12 +26,13 @@
"viem": "~2.37.13"
},
"peerDependencies": {
"tevm": ">=1.0.0"
"tevm": "^1.0.0-next.149"
},
"devDependencies": {
"@openzeppelin/contracts": "^5.6.1",
"@sveltejs/adapter-auto": "^7.0.1",
"@sveltejs/kit": "^2.60.1",
"@sveltejs/vite-plugin-svelte": "^7.2.0",
"@tevm/ts-plugin": "^1.0.0-next.148",
"@tevm/vite-plugin": "^1.0.0-next.148",
"rollup": "^4.62.3",
Expand Down
10 changes: 7 additions & 3 deletions apps/svelte-ethers/src/index.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { describe, expect, it } from 'vitest'
import { getAddress } from 'ethers'
import { addresses } from './addresses'

describe('sum test', () => {
it('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3)
describe('deployment addresses', () => {
it('contains valid Ethereum addresses for the configured chains', () => {
for (const address of Object.values(addresses)) {
expect(() => getAddress(address)).not.toThrow()
}
})
})
Loading
Loading