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
4 changes: 4 additions & 0 deletions examples/circle-smart-account/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
# Circle Modular Wallets — from https://console.circle.com/wallets/modular/configurator
# Also set Configurator → Passkey → Domain Name to `localhost` (required for Register/Login).
# Base Client URL only (do not include /polygonAmoy; the app appends the chain).
# Example: https://modular-sdk.circle.com/v1/rpc/w3s/buidl
VITE_CLIENT_KEY=
VITE_CLIENT_URL=
106 changes: 73 additions & 33 deletions examples/circle-smart-account/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,106 @@

This example Vite application demonstrates how to register and log in to a Circle Smart Account using passkeys. It also showcases how to perform a user operation with the account on Polygon Amoy.

## Run the example app
## Prerequisites

Please follow the instructions to run the example app on your local machine.
- A [Circle Console](https://console.circle.com/) account with Modular Wallets enabled
- See Circle’s [console setup guide](https://developers.circle.com/wallets/modular/console-setup) for the full Client Key + Passkey flow

### Environment Variables
## Environment variables

Before you start to run the app, you need to make sure that all environment variables are configured correctly.
Copy `.env.example` to `.env` in this folder (`examples/circle-smart-account/.env`):

Make a copy of `.env.example` and rename it to `.env`.
```bash
cp .env.example .env
```

Under `.env`, make sure the following environment variables are configured properly:
| Variable | Description |
| --- | --- |
| `VITE_CLIENT_KEY` | Client Key from the Circle Developer Console |
| `VITE_CLIENT_URL` | Base Client URL from the Circle Developer Console — **without** the chain suffix |

- `VITE_CLIENT_KEY`: Paste your Client Key here. You can create one in [Circle Developer Console](https://console.circle.com/wallets/modular/configurator).
- `VITE_CLIENT_URL`: Paste the Client URL here. You can copy it from [Circle Developer Console](https://console.circle.com/wallets/modular/configurator).
> [!IMPORTANT]
> `VITE_CLIENT_URL` must be a full `https://...` URL. The app appends `/polygonAmoy` internally for the modular transport. Passkey (RP) calls use the base URL only. If this variable is empty or already includes `/polygonAmoy`, setup will fail (for example `InvalidProviderError` for `/polygonAmoy`).

Once you have these environment variables setup, you can now follow the steps below to run the app locally.
Example `.env` shape (use your own credentials):

### Install dependencies
```env
VITE_CLIENT_KEY=your-client-key
VITE_CLIENT_URL=https://modular-sdk.circle.com/v1/rpc/w3s/buidl
```

You first need to make sure you have followed the [README](https://github.com/circlefin/modularwallets-web-sdk/blob/master/README.md) under project root and have installed all dependencies under root folder:
Restart `pnpm dev` after changing `.env`.

```bash
$ pnpm install
```
## Circle console setup

#### Important: Build the SDK
Do **both** of the following in [Configurator](https://console.circle.com/wallets/modular/configurator). Skipping Passkey Domain is a common cause of registration failures.

Since this project uses pnpm workspaces, you must build the SDK packages before running the example:
### 1. Passkey Domain (required)

```bash
# From the project root directory
$ pnpm build
Configurator → **Passkey** → **Domain Name**:

```text
localhost
```

This will generate the necessary distribution files that this example depends on.
Omit the port, then **Save**.

Now you need to go to this example folder:
> [!WARNING]
> If Passkey Domain is unset, `rp_getRegistrationOptions` fails with
> `Cannot find the entity config in the system.` (`code: -32012`).
> Creating a Client Key alone is not enough.

### 2. Client Key

1. Create a **Client Key** (Keys → Create a key → Client Key)
2. Under **Applicable Platforms → Web**, set **Allowed Domain** to `localhost` (no port)
3. Copy the Client Key and base Client URL into `.env` as above

The Passkey Domain and Client Key Allowed Domain should both be `localhost` for this example.

## Install and run

From the repository root:

```bash
$ cd examples/circle-smart-account
pnpm install
pnpm build
```

Once you are under the example folder, install all dependencies for the app:
Then from this folder:

```bash
$ pnpm install
cd examples/circle-smart-account
pnpm dev
```

### Run the app
Open `http://localhost:5173/`.

To run the app locally:
## Smoke test

```bash
$ pnpm dev
```
1. Enter a **username** that is **5–50 characters**, using only alphanumeric and `_@.:+-` (e.g. `Tester`, not `Test`)
2. Click **Register** and complete the passkey ceremony (browser / password manager)
3. Confirm a smart account **Address:** appears
4. Optional — send a user operation:
- Fund the smart account with **Polygon Amoy USDC** (faucet or transfer)
- **Address:** any Amoy EVM address you control (or the smart account itself)
- **Amount (USDC):** e.g. `0.01`
- Click **Send** and confirm a user operation hash / transaction hash appear

Now you should be able to see your app up and running in your browser at: `http://localhost:5173/`.
## Troubleshooting

### Important Notes
| Symptom | Likely cause |
| --- | --- |
| `Cannot find the entity config in the system` (`-32012`) | Passkey Domain not set in Configurator (use `localhost`) |
| `The username is invalid… 5 to 50 characters…` | Username too short or has disallowed characters |
| `InvalidProviderError` for `/polygonAmoy` | `VITE_CLIENT_URL` missing, includes `/polygonAmoy` already, or `.env` not in this folder |
| Client key / domain rejected | Client Key Allowed Domain must be `localhost` without a port |
| Passkey ceremony fails | Browser must support WebAuthn; use HTTPS or `localhost` |
| `Address mismatch` | SDK version older than `1.0.3` (MSCAUpgradable contract update) |
| User operation fails | Missing Amoy USDC balance, or paymaster / Circle client config |

- **Ensure the installed SDK version is greater than or equal to `1.0.3`:**
## Important notes

If you receive an error message that says `Address mismatch` in the example app, make sure you are using the correct version of the SDK as we updated the MSCAUpgradable smart contract implementation in version `1.0.3`. This error occurs when the SDK version is less than `1.0.3`.
- Ensure the installed SDK version is greater than or equal to `1.0.3`
- `.env` lives in `examples/circle-smart-account/`, not the repository root
- Official setup: [Create a modular wallet (console setup)](https://developers.circle.com/wallets/modular/console-setup)
2 changes: 1 addition & 1 deletion examples/circle-smart-account/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"@circle-fin/modular-wallets-core": "workspace:*",
"react": "19.2.7",
"react-dom": "19.2.7",
"viem": "2.23.13"
"viem": "2.45.3"
},
"devDependencies": {
"@types/react": "19.2.17",
Expand Down
2 changes: 1 addition & 1 deletion examples/eip-1193/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"@circle-fin/modular-wallets-core": "workspace:*",
"react": "19.2.7",
"react-dom": "19.2.7",
"viem": "2.23.13",
"viem": "2.45.3",
"web3": "4.16.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion examples/passkey-recovery/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"bip39": "^3.1.0",
"react": "19.2.7",
"react-dom": "19.2.7",
"viem": "2.23.13"
"viem": "2.45.3"
},
"devDependencies": {
"@types/react": "19.2.17",
Expand Down
4 changes: 2 additions & 2 deletions packages/w3s-web-core-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@circle-fin/modular-wallets-core",
"description": "Serverless Typescript SDK",
"version": "1.0.14",
"version": "1.0.15",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
Expand All @@ -24,7 +24,7 @@
},
"dependencies": {
"uuid": "^11.0.3",
"viem": "2.23.13",
"viem": "2.45.3",
"web3": "4.16.0",
"webauthn-p256": "0.0.5"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,39 @@ export const PersonalSignParams = [
'0x0f6Fed7D7526Aaa1692438AD1D77AaA0Ea9d0a56',
]

/**
* Mocks for EIP-1193 rpc params - personal_sign with Web3.js utf8ToHex("Hello World").
*/
export const PersonalSignWeb3TextParams = [
'0x48656c6c6f20576f726c64',
'0x0f6Fed7D7526Aaa1692438AD1D77AaA0Ea9d0a56',
]

/**
* Mocks for EIP-1193 rpc params - personal_sign with empty hex payload.
*/
export const PersonalSignEmptyHexParams = [
'0x',
'0x0f6Fed7D7526Aaa1692438AD1D77AaA0Ea9d0a56',
]

/**
* Mocks for EIP-1193 rpc params - personal_sign with a 1-byte payload.
*/
export const PersonalSignOneByteParams = [
'0x01',
'0x0f6Fed7D7526Aaa1692438AD1D77AaA0Ea9d0a56',
]

/**
* Mocks for EIP-1193 rpc params - personal_sign with a lowercase account address.
* Same account as PersonalSignParams (mixed-case / EIP-55), all-lowercase form.
*/
export const PersonalSignLowercaseAddressParams = [
'0xdeadbeef',
'0x0f6fed7d7526aaa1692438ad1d77aaa0ea9d0a56',
]

/**
* Mocks for EIP-1193 rpc responses - personal_sign.
*/
Expand Down Expand Up @@ -133,27 +166,37 @@ export const SignTypedDataV4WrongAddressParams = [
/**
* Mocks for EIP-1193 rpc params - eth_signTypedData_v4.
*/
const signTypedDataV4TypedData = {
domain: {
name: 'MockDomain',
version: '1',
chainId: 1,
verifyingContract: '0x9876543210fedcba9876543210fedcba98765432',
},
message: {
content: 'This is a test message',
},
primaryType: 'Message',
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
Message: [{ name: 'content', type: 'string' }],
},
}

export const SignTypedDataV4Params = [
'0x0f6Fed7D7526Aaa1692438AD1D77AaA0Ea9d0a56',
{
domain: {
name: 'MockDomain',
version: '1',
chainId: 1,
verifyingContract: '0x9876543210fedcba9876543210fedcba98765432',
},
message: {
content: 'This is a test message',
},
primaryType: 'Message',
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
Message: [{ name: 'content', type: 'string' }],
},
},
signTypedDataV4TypedData,
]

/**
* Mocks for EIP-1193 rpc params - eth_signTypedData_v4 with JSON string typed data.
*/
export const SignTypedDataV4StringParams = [
'0x0f6Fed7D7526Aaa1692438AD1D77AaA0Ea9d0a56',
JSON.stringify(signTypedDataV4TypedData),
]
Loading
Loading