diff --git a/.github/workflows/fmt.yml b/.github/workflows/fmt.yml
new file mode 100644
index 0000000..4e59157
--- /dev/null
+++ b/.github/workflows/fmt.yml
@@ -0,0 +1,13 @@
+name: format check
+
+on:
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ style:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dprint/check@v2.2
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e36586d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/node_modules
+/docs/dist
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..b636bb3
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Coinbase
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3bb11a4
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+This is a [Vocs](https://vocs.dev) project bootstrapped with the Vocs CLI.
diff --git a/bun.lockb b/bun.lockb
new file mode 100755
index 0000000..27e58a1
Binary files /dev/null and b/bun.lockb differ
diff --git a/docs/pages/FAQ.mdx b/docs/pages/FAQ.mdx
new file mode 100644
index 0000000..76cb7c3
--- /dev/null
+++ b/docs/pages/FAQ.mdx
@@ -0,0 +1,50 @@
+## When will Smart Wallet be publicly available on mainnets?
+By early June.
+
+## What networks are supported?
+- Base
+- Arbitrum
+- Optimism
+- Zora
+- Polygon
+- BNB
+- Avalanche
+- ETH mainnet (not preferred for use, due to gas cost)
+
+## How does Smart Wallet work with Coinbase Wallet mobile app and extension?
+For now, Smart Wallet is separate from wallet mobile and extension. Users sign on [keys.coinbase.com](https://keys.coinbase.com/)
+and can view and manage assets at [homebase.coinbase.com](https://homebase.coinbase.com/).
+
+See [makeWeb3Provider](/sdk/makeWeb3Provider#parameters) documentation for nuances based on different configurations.
+
+## Will users have the same address across chains?
+Yes, a user's Smart Wallet address will be the same across all the chains we support.
+
+## How much does it cost?
+Smart Wallet is free to use for both developers and users.
+
+## Is Smart Wallet more expensive to use?
+Smart contract wallets use more gas per transaction than EOA wallets. This means they should be avoided on networks like Ethereum mainnet,
+but elsewhere the cost difference is negligible.
+
+For example, some current transaction costs on Base using Smart Wallet
+- ETH transfer: $0.03
+- ERC-20 transfer: $0.06
+
+## Who's holding the keys?
+Smart wallets are secured by passkeys stored on the user's device.
+Passkeys are backed up with passkey providers such as Apple, Chrome, or 1Password,
+or on hardware such as YubiKeys.
+Passkey signatures are validated directly onchain via an [open source and audited smart contract](https://github.com/base-org/webauthn-sol).
+Coinbase never holds keys and never has access to user funds.
+
+## What is a passkey?
+Passkeys are alternatives to passwords or other encrypted methods like recovery phrases, that are extremely easy to create and highly secure.
+
+They are end-to-end encrypted and linked to your Google or iCloud account, Chrome profile, or hardware device such as a YubiKey.
+
+This means users no longer have to deal with passwords or recovery phrases. Instead they can use common methods of authorization like touch or faceID, and be more resistant to phishing attempts.
+
+## What happens if a user loses their passkey?
+
+Every Smart Wallet supports multiple owners, and our client will soon allow users to add a backup recovery key for the unlikely case they lose their passkey.
diff --git a/docs/pages/base-gasless-campaign.mdx b/docs/pages/base-gasless-campaign.mdx
new file mode 100644
index 0000000..89d3807
--- /dev/null
+++ b/docs/pages/base-gasless-campaign.mdx
@@ -0,0 +1,11 @@
+# Base Gasless Campaign
+
+Base is offering gas credits to help developers make the most of
+Smart Wallet's [paymaster (sponsored transactions)](/guides/paymasters) features.
+
+| Partner Tier | Base Gas Credit Incentive | Requirements | Actions |
+|--------------|---------------------------|--------------|----------|
+| 1 | $15k |
- Support Coinbase Smart Wallet
- Onboard to Coinbase Paymaster
- Preferred placement in your UI (ie “Create Wallet” button)
|
1. Bump your Coinbase SDK to add Coinbase Smart Wallet to your app, or bump to latest version of any [supporting wallet library](/wallet-library-support).
2. Sign in / up for [Coinbase Developer Platform](https://www.coinbase.com/developer-platform) (takes less than 2 minutes). No KYC needed - just email and phone.
3. Check out the Paymaster product where the Base Mainnet Paymaster is enabled by default. Set and change your gas policy at any time.
4. Complete [this form](https://docs.google.com/forms/d/1yPnBFW0bVUNLUN_w3ctCqYM9sjdIQO3Typ53KXlsS5g/viewform?edit_requested=true) and email basegascredits@coinbase.com that you’ve completed, along with evidence that you’ll have the “Create Wallet” button in your UI
Credits will land within 1 week of completion
|
+| 2 | $10k |
- Support Coinbase Smart Wallet
- Onboard to Coinbase Paymaster
|
1. Bump your Coinbase SDK to add Coinbase Smart Wallet to your app, or bump to latest version of any [supporting wallet library](/wallet-library-support).
2. Sign in / up for [Coinbase Developer Platform](https://www.coinbase.com/developer-platform) (takes less than 2 minutes). No KYC needed - just email and phone.
3. Check out the Paymaster product where the Base Mainnet Paymaster is enabled by default. Set and change your gas policy at any time.
4. Complete [this form](https://docs.google.com/forms/d/1yPnBFW0bVUNLUN_w3ctCqYM9sjdIQO3Typ53KXlsS5g/viewform?edit_requested=true) and email basegascredits@coinbase.com that you’ve completed.
Credits will land within 1 week of completion
|
+| 3 | $3k |
- Support Coinbase Smart Wallet
- Provide Contract Address
|
1. Bump your Coinbase SDK to add Coinbase Smart Wallet to your app, or bump to latest version of any [supporting wallet library](/wallet-library-support).
2. Provide your contract address(es) in [this form](https://docs.google.com/forms/d/1wiEf01LCA4FBQ2Yh_K20TC9USgDPR95LkVVfe_o2-Is/viewform?edit_requested=true).
3. For additional credits, sign in / up for [Coinbase Developer Platform](https://www.coinbase.com/developer-platform) (takes less than 2 minutes). No KYC needed - just email and phone.
|
+| Bonus | $1k |
- Release demo
| Create a demo of your Coinbase Smart wallet integration, post on social (Warpcast and/or X) and tag Coinbase Wallet and/or Base|
\ No newline at end of file
diff --git a/docs/pages/checklist.mdx b/docs/pages/checklist.mdx
new file mode 100644
index 0000000..378465f
--- /dev/null
+++ b/docs/pages/checklist.mdx
@@ -0,0 +1,35 @@
+# Smart Wallet Launch-Ready Checklist
+## The Basics
+
+:::steps
+
+### Test your app with Smart Wallet
+
+Test your app with Smart Wallet enabled. See [our guide](/guides/update-existing-app).
+
+### Ensure Smart Wallet compatible Signature Validation
+
+If your app does any offchain signature validation (such as Sign-In With Ethereum) or your smart contracts
+do any signature validation, ensure your app is [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) compliant.
+See [our guide](/guides/signature-verification).
+
+### Magic Spend Compatible
+Magic Spend enables users to get "just in time" transaction funding from their Coinbase account.
+This is currently supported for ETH only on Base.
+To give users the best experience, apps should make use of the `auxiliaryFunds` capability
+and ensure they are not blocking actions based solely on onchain balance checks. See [our guide](/guides/magic-spend).
+
+### Use SDK 4.0.3 or later
+Ensure your app is using SDK version 4.0.3. Check [Wallet Library Support](/wallet-library-support) to ensure readiness.
+:::
+
+## Bonus: Make the most of Smart Wallet!
+:::steps
+### Batch Transactions
+Improve your app's UX by using batch transactions, allowing, for examples,
+approvals and swaps to happen in a single transaction. See [our guide](/guides/batch-transactions).
+### Paymasters (sponsored transactions)
+Smart Wallet is one of the first wallets to support app-defined paymasters. This enables sponsoring
+certain transactions for your users, so they do not have to know or think about gas cost.
+See [our guide](/guides/paymasters).
+:::
\ No newline at end of file
diff --git a/docs/pages/guides/batch-transactions.mdx b/docs/pages/guides/batch-transactions.mdx
new file mode 100644
index 0000000..877627e
--- /dev/null
+++ b/docs/pages/guides/batch-transactions.mdx
@@ -0,0 +1,178 @@
+# Batch Transactions
+
+With Smart Wallet, you can send multiple onchain calls in a single transaction. Doing so improves the UX of multi-step interactions by reducing them to a single click. A common example of where you might want to leverage batch transactions is an ERC-20 `approve` followed by a swap.
+
+You can submit batch transactions by using new `wallet_sendCalls` RPC, defined [here](https://eip5792.xyz/reference/sendCalls).
+
+## Using Wagmi
+
+:::warning
+The `useWriteContracts` and `useCapabilities` hooks used below rely on new wallet RPC and are not yet supported in most wallets.
+It is recommended to have a fallback function if your app supports wallets other than Smart Wallet.
+:::
+
+:::steps
+### (Optional) Check for atomic batching support
+
+Smart Wallet will submit multiple calls as part of a single transaction. However, if your app supports other wallets, and you want to check that multiple calls will be submitted atomically (in a single transaction), check the wallet's capabilities.
+
+```ts twoslash [App.tsx]
+// @filename: App.tsx
+import React from 'react'
+// ---cut---
+import { useCapabilities } from 'wagmi/experimental'
+
+function App() {
+ const { data: capabilities } = useCapabilities() // [!code hl]
+// @log: {
+// @log: 84532: {
+// @log: atomicBatch: {
+// @log: supported: true,
+// @log: },
+// @log: }
+// @log: }
+
+ return
+}
+```
+
+The `useCapabilities` method will return, per chain, the capabilities that the connected wallet supports. If the connected wallet supports atomic batching, it will return an `atomicBatch` capability with a `supported` field equal to `true` for each chain it supports atomic batching on.
+
+### Send the calls
+
+If you have your smart contract ABIs, the easiest way to send multiple calls is to use the Wagmi `useWriteContracts` hook.
+
+```ts twoslash [App.tsx]
+// @filename: App.tsx
+import React from 'react'
+// ---cut---
+import { useAccount } from 'wagmi'
+import { useWriteContracts } from 'wagmi/experimental'
+
+const abi = [
+ {
+ stateMutability: 'nonpayable',
+ type: 'function',
+ inputs: [{ name: 'to', type: 'address' }],
+ name: 'safeMint',
+ outputs: [],
+ }
+] as const
+
+function App() {
+ const account = useAccount()
+ const { writeContracts } = useWriteContracts() // [!code hl]
+
+ const handleMint = () => {
+ writeContracts({ // [!code hl]
+ contracts: [ // [!code hl]
+ { // [!code hl]
+ address: "0x119Ea671030FBf79AB93b436D2E20af6ea469a19", // [!code hl]
+ abi, // [!code hl]
+ functionName: "safeMint", // [!code hl]
+ args: [account.address], // [!code hl]
+ }, // [!code hl]
+ { // [!code hl]
+ address: "0x119Ea671030FBf79AB93b436D2E20af6ea469a19", // [!code hl]
+ abi, // [!code hl]
+ functionName: "safeMint", // [!code hl]
+ args: [account.address], // [!code hl]
+ } // [!code hl]
+ ], // [!code hl]
+ }) // [!code hl]
+ }
+
+ return (
+
+
+
+ )
+}
+```
+
+### Check on the status of your calls
+
+The `useWriteContracts` hook returns an object with a `data` field. This `data` is a call bundle identifier. Use the Wagmi `useCallsStatus` hook with this identifier to check on the status of your calls.
+
+This will return a `PENDING` or `CONFIRMED` status along with a subset of a transaction receipt.
+
+```ts twoslash [App.tsx]
+// @filename: App.tsx
+import React from 'react'
+// ---cut---
+import { useAccount } from 'wagmi'
+import { useWriteContracts, useCallsStatus } from 'wagmi/experimental'
+
+const abi = [
+ {
+ stateMutability: 'nonpayable',
+ type: 'function',
+ inputs: [{ name: 'to', type: 'address' }],
+ name: 'safeMint',
+ outputs: [],
+ }
+] as const
+
+function App() {
+ const account = useAccount()
+ const { data: id, writeContracts } = useWriteContracts()
+ const { data: callsStatus } = useCallsStatus({ // [!code hl]
+ id: id as string, // [!code hl]
+ query: { // [!code hl]
+ enabled: !!id, // [!code hl]
+ // Poll every second until the calls are confirmed // [!code hl]
+ refetchInterval: (data) => // [!code hl]
+ data.state.data?.status === "CONFIRMED" ? false : 1000, // [!code hl]
+ }, // [!code hl]
+ }); // [!code hl]
+// @log: {
+// @log: status: 'CONFIRMED',
+// @log: receipts: [
+// @log: {
+// @log: logs: [
+// @log: {
+// @log: address: '0x...',
+// @log: topics: [
+// @log: '0x...'
+// @log: ],
+// @log: data: '0x...'
+// @log: },
+// @log: ],
+// @log: status: 'success',
+// @log: blockHash: '0x...',
+// @log: blockNumber: 122414523n,
+// @log: gasUsed: 390000n,
+// @log: transactionHash: '0x...'
+// @log: }
+// @log: ]
+// @log: }
+
+ const handleMint = () => {
+ writeContracts({
+ contracts: [
+ {
+ address: "0x...",
+ abi,
+ functionName: "safeMint",
+ args: [account.address],
+ },
+ {
+ address: "0x...",
+ abi,
+ functionName: "safeMint",
+ args: [account.address],
+ }
+ ],
+ })
+ }
+
+ return (
+
+
+ {callsStatus &&
Status: {callsStatus.status}
}
+
+ )
+}
+```
+
+:::
\ No newline at end of file
diff --git a/docs/pages/guides/components/create-wallet-button.mdx b/docs/pages/guides/components/create-wallet-button.mdx
new file mode 100644
index 0000000..6b07ba1
--- /dev/null
+++ b/docs/pages/guides/components/create-wallet-button.mdx
@@ -0,0 +1,530 @@
+import { Callout } from 'vocs/components';
+
+# Create Wallet Button
+
+## Simplify user onboarding with Smart Wallet
+
+For the best onboarding experience, we recommend adding a highly visible 'Create' or 'Create Wallet' button to your dapp's homepage.
+
+
+
+Adding a 'Create Wallet' button to a page streamlines the onboarding experience for a new user and gets them ready to use your dapp in a few seconds.
+
+
+We're offering additional incentives to dapps who implement this new approach. By integrating a create wallet button on the front page of your app, you are eligible for:
+
+- Increased volume of gas credits via our Cloud paymaster
+- Comarketing and promotional opportunities with Coinbase
+- Featured placement in our dapp listings on homebase.coinbase.com
+
+For more information, please [fill out the
+form](https://docs.google.com/forms/d/e/1FAIpQLSdmTnSjbI-wkkf2TzmuSEv3iYzkbZBsY08xPdK-54J0Ipt4PA/viewform)
+on [coinbase.com/smart-wallet](http://coinbase.com/smart-wallet) so our team can get
+in touch with you.
+
+
+
+## Implementation
+
+**We recommend two paths for implementation:**
+
+1. Match our branded Create Wallet button
+1. Match your own apps look and feel in the Create button
+
+Example:
+
+
+ );
+}
+```
+
+```ts twoslash [wagmi.ts]
+import { http, createConfig } from 'wagmi';
+import { baseSepolia } from 'wagmi/chains';
+import { coinbaseWallet } from 'wagmi/connectors';
+
+export const config = createConfig({
+ chains: [baseSepolia],
+ connectors: [
+ coinbaseWallet({
+ appName: 'Create Wagmi',
+ preference: 'smartWalletOnly',
+ }),
+ ],
+ transports: {
+ [baseSepolia.id]: http(),
+ },
+});
+
+declare module 'wagmi' {
+ interface Register {
+ config: typeof config;
+ }
+}
+```
+
+:::
diff --git a/docs/pages/guides/create-app/using-boat.mdx b/docs/pages/guides/create-app/using-boat.mdx
new file mode 100644
index 0000000..7023b93
--- /dev/null
+++ b/docs/pages/guides/create-app/using-boat.mdx
@@ -0,0 +1,24 @@
+# Getting started with Build Onchain Apps Template
+This guide uses [Build Onchain Apps Template](https://buildonchainapps.xyz/).
+::::steps
+## Create project
+Run the command below and proceed through the following steps, choosing "yes"
+when asked "Do you want to use Smart Wallet?".
+```bash
+npx @coinbase/build-onchain-apps@latest create
+```
+
+## Setup project
+Open your newly created project, and create a new `.env` file in the `web` folder. Add the following to this file.
+```bash
+NEXT_PUBLIC_RPC_URL=https://sepolia.base.org
+ENVIRONMENT=localhost
+```
+
+## Install dependencies and run dev
+```bash
+yarn install && yarn dev
+```
+Visit your site locally and click the "Connect Wallet" button. You'll see a popup window with a Smart Wallet
+connection option.
+::::
\ No newline at end of file
diff --git a/docs/pages/guides/create-app/using-wagmi.mdx b/docs/pages/guides/create-app/using-wagmi.mdx
new file mode 100644
index 0000000..5adfee6
--- /dev/null
+++ b/docs/pages/guides/create-app/using-wagmi.mdx
@@ -0,0 +1,69 @@
+# Getting Started with Wagmi Template
+
+This guide covers creating a new Wagmi template project that uses Smart Wallet.
+
+::::steps
+## Run the following command and proceed through the prompts.
+
+:::code-group
+
+```bash [pnpm]
+pnpm create wagmi
+```
+
+```bash [bun]
+bun create wagmi
+```
+
+:::
+
+## Install packages and run dev.
+
+:::code-group
+
+```bash [pnpm]
+pnpm install && pnpm run dev
+```
+
+```bash [bun]
+bun install && bun run dev
+```
+
+:::
+
+## Update the Wagmi config and coinbaseWallet connector to use baseSepolia
+
+`src/wagmi.ts` should look something like this:
+
+```ts twoslash
+import { http, createConfig } from 'wagmi';
+import { baseSepolia } from 'wagmi/chains';
+import { coinbaseWallet } from 'wagmi/connectors';
+
+export const config = createConfig({
+ chains: [baseSepolia],
+ connectors: [
+ coinbaseWallet({ appName: 'Create Wagmi', preference: 'smartWalletOnly' }),
+ ],
+ transports: {
+ [baseSepolia.id]: http(),
+ },
+});
+
+declare module 'wagmi' {
+ interface Register {
+ config: typeof config;
+ }
+}
+```
+
+## Visit your site locally, click the "Coinbase Wallet" button, and choose "Create a Smart Wallet."
+
+Note, if you are using a browser with Coinbase Wallet Extension enabled,
+that will be the default connection method and you will not see a screen to choose Smart Wallet.
+Temporarily disable the Extension to connect with Smart Wallet.
+
+## Keep building
+
+You can find everything you need here: https://wagmi.sh/react/api/hooks
+::::
\ No newline at end of file
diff --git a/docs/pages/guides/magic-spend.mdx b/docs/pages/guides/magic-spend.mdx
new file mode 100644
index 0000000..75111ce
--- /dev/null
+++ b/docs/pages/guides/magic-spend.mdx
@@ -0,0 +1,33 @@
+# Magic Spend
+
+With Magic Spend, Smart Wallet users can use their Coinbase balances onchain. This means users can easily start using onchain apps without needing to onramp funds into their wallet.
+
+This also means that apps might not have all the balance information typically available to them by reading onchain data. Smart Wallet indicates that this is the case by responding to [`wallet_getCapabilities` RPC calls](https://eip5792.xyz/reference/getCapabilities) with the `auxiliaryFunds` capability for each chain Smart Wallet users can use their Coinbase balances on.
+
+If your app supports Smart Wallet, it should not assume it knows the full balances available to a user if the `auxiliaryFunds` capability is present on a given chain.
+
+## Using Wagmi
+
+```ts twoslash [App.tsx]
+// @filename: App.tsx
+import React from 'react'
+// ---cut---
+import { useCapabilities } from 'wagmi/experimental'
+
+function App() {
+ const { data: capabilities } = useCapabilities()
+// @log: {
+// @log: 84532: {
+// @log: auxiliaryFunds: {
+// @log: supported: true,
+// @log: },
+// @log: }
+// @log: }
+
+ return
+}
+```
+
+If your app supports Smart Wallet and sees that the `auxiliaryFunds` capability is supported on a given chain,
+it means that a user might have funds available for use onchain on Coinbase.
+As a result, your app should not block user actions on the basis of balance checks.
\ No newline at end of file
diff --git a/docs/pages/guides/paymasters.mdx b/docs/pages/guides/paymasters.mdx
new file mode 100644
index 0000000..889cc48
--- /dev/null
+++ b/docs/pages/guides/paymasters.mdx
@@ -0,0 +1,351 @@
+# Paymasters (Sponsored Transactions)
+
+One of the biggest UX enhancements unlocked by Smart Wallet is the ability for app developers to sponsor their users' transactions. If your app supports Smart Wallet, you can start sponsoring your users' transactions by using [standardized paymaster service communication](https://erc7677.xyz) enabled by [new wallet RPC methods](https://eip5792.xyz).
+The code below is also in our [Wagmi Smart Wallet template](https://github.com/wilsoncusack/wagmi-scw/).
+
+## Using Wagmi + Permissionless in a Next.js app
+
+:::warning
+The `useWriteContracts` and `useCapabilities` hooks used below rely on new wallet RPC and are not yet supported in most wallets.
+It is recommended to have a fallback function if your app supports wallets other than Smart Wallet.
+:::
+
+::::steps
+### Choose a paymaster service provider
+
+As a prerequisite, you'll need to obtain a paymaster service URL from a paymaster service provider. To be compatible with Smart Wallet, the paymaster provider you choose must be ERC-7677-compliant.
+
+We recommend the [Coinbase Developer Platform](https://www.coinbase.com/developer-platform) paymaster, currently offering up to $15k in gas credits as part of the [Base Gasless Campaign](/base-gasless-campaign).
+You can find a full list of ERC-7677-compliant paymaster services [here](https://www.erc7677.xyz/ecosystem/paymasters).
+
+Once you choose a paymaster service provider and obtain a paymaster service URL, you can proceed to integration.
+
+### (Recommended) Setup your paymaster proxy
+
+Creating an API to proxy calls to your paymaster service is important for two reasons.
+1. Allows you to protect any API secret.
+2. Allows you to add extra validation on what requests you want to sponsor.
+
+#### Validate UserOperation
+Before we write our proxy, let's write a `willSponsor` function to add some extra validation.
+The policies on many paymaster services are quite simple and limited. As your API will be exposed on the web,
+you want to make sure in cannot abused: called to sponsor transaction you do not want to fund. The checks below
+are a bit tedious, but highly recommended to be safe. See "Trust and Validation" [here](https://hackmd.io/@AhweV9sISeevhvrtVPCGDw/BynRsX7Ca#Trust-and-Validation)
+for more on this.
+
+The code below is built specifically for Smart Wallet. It would need to be updated to support other smart accounts.
+:::code-group
+```ts twoslash [utils.ts]
+import { ENTRYPOINT_ADDRESS_V06, UserOperation } from "permissionless";
+import {
+ Address,
+ BlockTag,
+ Hex,
+ decodeAbiParameters,
+ decodeFunctionData,
+} from "viem";
+import { baseSepolia } from "viem/chains";
+import {client} from "./config"
+import {
+ coinbaseSmartWalletABI,
+ coinbaseSmartWalletFactoryAddress,
+ coinbaseSmartWalletProxyBytecode,
+ coinbaseSmartWalletV1Implementation,
+ erc1967ProxyImplementationSlot,
+ magicSpendAddress
+} from "./constants"
+import { myNFTABI, myNFTAddress } from "./myNFT";
+
+// @noErrors
+export async function willSponsor({
+ chainId,
+ entrypoint,
+ userOp,
+}: { chainId: number; entrypoint: string; userOp: UserOperation<"v0.6"> }) {
+ // check chain id
+ if (chainId !== baseSepolia.id) return false;
+ // check entrypoint
+ // not strictly needed given below check on implementation address, but leaving as example
+ if (entrypoint.toLowerCase() !== ENTRYPOINT_ADDRESS_V06.toLowerCase())
+ return false;
+
+ try {
+ // check the userOp.sender is a proxy with the expected bytecode
+ const code = await client.getBytecode({ address: userOp.sender });
+
+ if (!code) {
+ // no code at address, check that the initCode is deploying a Coinbase Smart Wallet
+ // factory address is first 20 bytes of initCode after '0x'
+ const factoryAddress = userOp.initCode.slice(0, 42);
+ if (factoryAddress.toLowerCase() !== coinbaseSmartWalletFactoryAddress.toLowerCase())
+ return false;
+ } else {
+ // code at address, check that it is a proxy to the expected implementation
+ if (code != coinbaseSmartWalletProxyBytecode) return false;
+
+ // check that userOp.sender proxies to expected implementation
+ const implementation = await client.request<{
+ Parameters: [Address, Hex, BlockTag];
+ ReturnType: Hex;
+ }>({
+ method: "eth_getStorageAt",
+ params: [userOp.sender, erc1967ProxyImplementationSlot, "latest"],
+ });
+ const implementationAddress = decodeAbiParameters(
+ [{ type: "address" }],
+ implementation
+ )[0];
+ if (implementationAddress != coinbaseSmartWalletV1Implementation)
+ return false;
+ }
+
+ // check that userOp.callData is making a call we want to sponsor
+ const calldata = decodeFunctionData({
+ abi: coinbaseSmartWalletABI,
+ data: userOp.callData,
+ });
+
+ // keys.coinbase.com always uses executeBatch
+ if (calldata.functionName !== "executeBatch") return false;
+ if (!calldata.args || calldata.args.length == 0) return false;
+
+ const calls = calldata.args[0] as {
+ target: Address;
+ value: bigint;
+ data: Hex;
+ }[];
+ // modify if want to allow batch calls to your contract
+ if (calls.length > 2) return false;
+
+ let callToCheckIndex = 0;
+ if (calls.length > 1) {
+ // if there is more than one call, check if the first is a magic spend call
+ if (calls[0].target.toLowerCase() !== magicSpendAddress.toLowerCase())
+ return false;
+ callToCheckIndex = 1;
+ }
+
+ if (
+ calls[callToCheckIndex].target.toLowerCase() !==
+ myNFTAddress.toLowerCase()
+ )
+ return false;
+
+ const innerCalldata = decodeFunctionData({
+ abi: myNFTABI,
+ data: calls[callToCheckIndex].data,
+ });
+ if (innerCalldata.functionName !== "safeMint") return false;
+
+ return true;
+ } catch (e) {
+ console.error(`willSponsor check failed: ${e}`);
+ return false;
+ }
+}
+```
+```ts twoslash [constants.ts] filename="constants.ts"
+export const coinbaseSmartWalletProxyBytecode =
+ "0x363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3";
+export const coinbaseSmartWalletV1Implementation =
+ "0x000100abaad02f1cfC8Bbe32bD5a564817339E72";
+export const coinbaseSmartWalletFactoryAddress =
+ "0x0BA5ED0c6AA8c49038F819E587E2633c4A9F428a";
+export const magicSpendAddress = "0x011A61C07DbF256A68256B1cB51A5e246730aB92";
+export const erc1967ProxyImplementationSlot =
+ "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
+
+export const coinbaseSmartWalletABI = [
+ {
+ type: "function",
+ name: "executeBatch",
+ inputs: [
+ {
+ name: "calls",
+ type: "tuple[]",
+ internalType: "struct CoinbaseSmartWallet.Call[]",
+ components: [
+ {
+ name: "target",
+ type: "address",
+ internalType: "address",
+ },
+ {
+ name: "value",
+ type: "uint256",
+ internalType: "uint256",
+ },
+ {
+ name: "data",
+ type: "bytes",
+ internalType: "bytes",
+ },
+ ],
+ },
+ ],
+ outputs: [],
+ stateMutability: "payable",
+ },
+];
+```
+```ts twoslash [myNFT.ts] filename="myNFT.ts"
+export const myNFTABI = [
+ {
+ stateMutability: "nonpayable",
+ type: "function",
+ inputs: [{ name: "to", type: "address" }],
+ name: "safeMint",
+ outputs: [],
+ },
+] as const;
+
+export const myNFTAddress = "0x119Ea671030FBf79AB93b436D2E20af6ea469a19";
+
+```
+```ts twoslash [config.ts] filename="config.ts"
+import { createPublicClient, http } from "viem";
+import { baseSepolia } from "viem/chains";
+
+export const client = createPublicClient({
+ chain: baseSepolia,
+ transport: http(),
+});
+```
+:::
+
+#### Create Proxy
+The proxy you create will need to handle the `pm_getPaymasterStubData` and `pm_getPaymasterData` JSON-RPC requests specified by ERC-7677.
+:::code-group
+```ts twoslash [route.ts]
+import { paymasterClient } from "./config";
+// @noErrors
+import { willSponsor } from "./utils";
+
+export async function POST(r: Request) {
+ const req = await r.json();
+ const method = req.method;
+ const [userOp, entrypoint, chainId] = req.params;
+ console.log(req.params);
+ if (!willSponsor({ chainId: parseInt(chainId), entrypoint, userOp })) {
+ return Response.json({ error: "Not a sponsorable operation" });
+ }
+
+ if (method === "pm_getPaymasterStubData") {
+ const result = await paymasterClient.getPaymasterStubData({
+ userOperation: userOp,
+ });
+ return Response.json({ result });
+ } else if (method === "pm_getPaymasterData") {
+ const result = await paymasterClient.getPaymasterData({
+ userOperation: userOp,
+ });
+ return Response.json({ result });
+ }
+ return Response.json({ error: "Method not found" });
+}
+
+```
+```ts twoslash [config.ts] filename="config.ts"
+const process = { env: { PAYMASTER_SERVICE_URL: '' } }
+// ---cut---
+import { createClient, http } from "viem";
+import { baseSepolia } from "viem/chains";
+import { ENTRYPOINT_ADDRESS_V06 } from "permissionless";
+import { paymasterActionsEip7677 } from "permissionless/experimental";
+
+const paymasterService = process.env.PAYMASTER_SERVICE_URL!;
+
+export const paymasterClient = createClient({
+ chain: baseSepolia,
+ transport: http(paymasterService),
+}).extend(paymasterActionsEip7677(ENTRYPOINT_ADDRESS_V06));
+```
+:::
+
+### Send EIP-5792 requests with a paymaster service capability
+
+Once you have your paymaster service set up, you can now pass its URL along to Wagmi's `useWriteContracts` hook.
+
+If you set up a proxy in your app's backend as recommended in step (2) above, you'll want to pass in the proxy URL you created.
+
+:::code-group
+```ts twoslash [App.tsx]
+import { useAccount } from "wagmi";
+import { useCapabilities, useWriteContracts } from "wagmi/experimental";
+import { useMemo, useState } from "react";
+import { CallStatus } from "./CallStatus";
+import { myNFTABI, myNFTAddress } from "./myNFT";
+
+export function App() {
+ const account = useAccount();
+ const [id, setId] = useState(undefined);
+ const { writeContracts } = useWriteContracts({
+ mutation: { onSuccess: (id) => setId(id) },
+ });
+ const { data: availableCapabilities } = useCapabilities({
+ account: account.address,
+ });
+ const capabilities = useMemo(() => {
+ if (!availableCapabilities || !account.chainId) return {};
+ const capabilitiesForChain = availableCapabilities[account.chainId];
+ if (
+ capabilitiesForChain["paymasterService"] &&
+ capabilitiesForChain["paymasterService"].supported
+ ) {
+ return {
+ paymasterService: {
+ url: `${document.location.origin}/api/paymaster`,
+ },
+ };
+ }
+ return {};
+ }, [availableCapabilities, account.chainId]);
+
+ return (
+
+
Transact With Paymaster
+
{JSON.stringify(capabilities)}
+
+
+ {id && }
+
+
+ );
+}
+```
+```ts twoslash [myNFT.ts] filename="myNFT.ts"
+export const myNFTABI = [
+ {
+ stateMutability: "nonpayable",
+ type: "function",
+ inputs: [{ name: "to", type: "address" }],
+ name: "safeMint",
+ outputs: [],
+ },
+] as const;
+
+export const myNFTAddress = "0x119Ea671030FBf79AB93b436D2E20af6ea469a19";
+
+```
+:::
+
+That's it! Smart Wallet will handle the rest. If your paymaster service is able to sponsor the transaction,
+in the UI Smart Wallet will indicate to your user that the transaction is sponsored.
+
+::::
\ No newline at end of file
diff --git a/docs/pages/guides/signature-verification.mdx b/docs/pages/guides/signature-verification.mdx
new file mode 100644
index 0000000..2b493fe
--- /dev/null
+++ b/docs/pages/guides/signature-verification.mdx
@@ -0,0 +1,87 @@
+# Signature Verification
+
+There are important details to verifying smart contract wallet signatures. The smart contract itself
+cannot produce a signature. Instead, the contract has a function
+```solidity
+function isValidSignature(bytes32 _hash, bytes memory _signature) returns (bytes4 magicValue);
+```
+which can be called to determine if signature should be considered valid (defined in [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271)).
+
+In the case of Smart Wallet, a signature is considered valid if it was signed by a current signer (aka "owner") of the Smart Wallet.
+For example, a user can sign a message with their passkey, and when `isValidSignature` is called on their Smart Wallet,
+it would return that the signature is valid because their passkey is an owner.
+
+There is also an additional complexity: users receive their Smart Wallet address immediately upon passkey registration,
+and are able to begin signing for their Smart Wallet,
+but their Smart Wallet is not deployed on a chain until the first transaction on that chain.
+
+[ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) describes
+> With the rising popularity of account abstraction, we often find that the best user experience
+for contract wallets is to defer contract deployment until the first user transaction,
+therefore not burdening the user with an additional deploy step before they can use their account.
+However, at the same time, many dApps expect signatures, not only for interactions, but also just for logging in.
+
+So the challenge is, how do we verify signatures in a way that works for both deployed and undeployed Smart Wallets?
+ERC-6492 has a solution for this, which Smart Wallet has adopted.
+We won't go unto all the details here, read the ERC linked above, if you're looking for that.
+Below we cover the minimum work needed to support on and off chain signature validation for Smart Wallet.
+
+## Offchain
+For purely offchain signature verification––such as "Sign-In With Ethereum"––ensure you are using
+a ERC-6492-compliant signature verification library.
+We recommend Viem's [`verifyMessage`](https://viem.sh/docs/actions/public/verifyMessage#verifymessage) ([example](https://github.com/wilsoncusack/wagmi-scw/blob/main/src/components/SignMessage.tsx#L28))
+and [verifyTypedData](https://viem.sh/docs/actions/public/verifyTypedData) ([example](https://github.com/wilsoncusack/wagmi-scw/blob/main/src/components/TypedSign.tsx#L46)).
+
+See our [Sign-In with Ethereum guide](/guides/siwe) for a detailed example.
+
+## Onchain
+For signatures that will be used onchain, such as with [Permit2](https://github.com/Uniswap/permit2)
+or [Seaport](https://github.com/ProjectOpenSea/seaport) developers will need to inspect the signature offchain
+and remove unneeded ERC-6492 data, if it is present.
+We recommend using the [parseErc6492Signature](https://viem.sh/experimental/erc6492/parseErc6492Signature#parseerc6492signature)
+util from Viem. Abbreviated example below. See full example [here](https://github.com/wilsoncusack/wagmi-scw/blob/main/src/components/Permit2.tsx).
+
+```ts [example.tsx]
+import { useMemo, useState } from "react";
+import type { Hex } from "viem";
+import { useAccount, useReadContract, useSignTypedData } from "wagmi";
+import { useWriteContract } from "wagmi";
+import { parseErc6492Signature } from "viem/experimental";
+
+export function App() {
+ const { writeContract } = useWriteContract();
+ const [signature, setSignature] = useState(undefined)
+ const { signTypedData } = useSignTypedData({
+ mutation: { onSuccess: (sig) => setSignature(sig) },
+ });
+
+ // parse signature, in case encoded with extra ERC-6492 data
+ const parsedSignature = useMemo(() => {
+ if (!signature) return
+
+ return parseErc6492Signature(signature).signature
+ }, [signature])
+
+ return(
+ <>
+
+ {signature && (
+
+ )}
+ >
+ )
+}
+```
diff --git a/docs/pages/guides/siwe.mdx b/docs/pages/guides/siwe.mdx
new file mode 100644
index 0000000..bf8cf21
--- /dev/null
+++ b/docs/pages/guides/siwe.mdx
@@ -0,0 +1,209 @@
+import { Callout } from 'vocs/components';
+
+# Using Smart Wallet with Sign-In with Ethereum
+
+This guide covers creating a new Sign-In with Ethereum template project that uses Smart Wallet.
+
+
+ This simple implementation is for demo purposes only and is not meant to be an example of a
+ production app. A production app should: 1. Generate a random nonce for each message on the
+ backend. 2. Check the nonce and verify the message signature on the backend as specified in
+ [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361). 3. Invalidate nonces on logout to prevent
+ replay attacks through session duplication.
+
+
+::::steps
+## Create a project
+
+Follow the [Wagmi guide](/guides/create-app/using-wagmi) to set up a template project and connect a Smart Wallet.
+
+## Install dependencies
+
+:::code-group
+
+```bash [pnpm]
+pnpm install siwe
+```
+
+```bash [bun]
+bun install siwe
+```
+
+:::
+
+
+## Create a new SIWE component
+
+:::code-group
+
+
+```tsx twoslash [src/SignInWithEthereum.tsx]
+import React from 'react';
+
+export function SignInWithEthereum() {
+ return (
+
+ // [!code focus]
+ >
+ )
+}
+
+export default App
+
+```
+
+:::
+
+
+## Prompt to sign and store signature
+Wagmi's `signMessage` function will open the Smart Wallet popup to sign the message. The signature is stored in the component's state.
+
+```tsx twoslash [src/SignInWithEthereum.tsx]
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import { useAccount, usePublicClient, useSignMessage } from 'wagmi';
+import { SiweMessage } from 'siwe';
+import type { Hex } from 'viem';
+
+export function SignInWithEthereum() {
+ const [signature, setSignature] = useState(undefined);
+ const { signMessage } = useSignMessage({ mutation: { onSuccess: (sig) => setSignature(sig) } });
+ const account = useAccount();
+
+ const siweMessage = useMemo(() => {
+ return new SiweMessage({
+ domain: document.location.host,
+ address: account.address,
+ chainId: account.chainId,
+ uri: document.location.origin,
+ version: '1',
+ statement: 'Smart Wallet SIWE Example',
+ nonce: '12345678', // replace with nonce generated by your backend
+ });
+ }, []);
+
+ const promptToSign = () => {
+ signMessage({ message: siweMessage.prepareMessage() });
+ };
+
+ return (
+
+
SIWE Example
+
+ {signature &&
Signature: {signature}
}
+
+ );
+}
+```
+
+## Verify the message signature
+
+For Smart Wallet, [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) should be used to verify the SIWE message signature.
+
+
+ This simple example does not check the nonce during verification, all production implementations
+ should. Furthermore, nonces should be invalidated on logout to prevent replay attacks through
+ session duplication (e.g. store expired nonce and make sure they can't be used again). In
+ production apps, SIWE message verification is generally handled on the backend.
+
+
+```tsx twoslash [src/SignInWithEthereum.tsx]
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import type { Hex } from 'viem';
+import { useAccount, usePublicClient, useSignMessage } from 'wagmi';
+import { SiweMessage } from 'siwe';
+export function SignInWithEthereum() {
+ const [signature, setSignature] = useState(undefined);
+ const [valid, setValid] = useState(undefined); // [!code focus]
+ const client = usePublicClient(); // [!code focus]
+ const { signMessage } = useSignMessage({ mutation: { onSuccess: (sig) => setSignature(sig) } });
+ const account = useAccount();
+
+ const message = useMemo(() => {
+ return new SiweMessage({
+ domain: document.location.host,
+ address: account.address,
+ chainId: account.chainId,
+ uri: document.location.origin,
+ version: '1',
+ statement: 'Smart Wallet SIWE Example',
+ nonce: '12345678', // replace with nonce generated by your backend
+ });
+ }, []);
+
+ const checkValid = useCallback(async () => { // [!code focus]
+ if (!signature || !account.address || !client) return; // [!code focus]
+ const isValid = await client.verifyMessage({ // [!code focus]
+ // [!code focus]
+ address: account.address, // [!code focus]
+ message: message.prepareMessage(), // [!code focus]
+ signature, // [!code focus]
+ }); // [!code focus]
+ setValid(isValid); // [!code focus]
+ }, [signature, account]); // [!code focus]
+
+ useEffect(() => { // [!code focus]
+ checkValid(); // [!code focus]
+ }, [signature, account]); // [!code focus]
+
+ const promptToSign = () => {
+ signMessage({ message: message.prepareMessage() });
+ };
+ return (
+
+
SIWE Example
+
+ {signature &&
Signature: {signature}
}
+ {valid !== undefined &&
Is valid: {valid.toString()}
}{/* // [!code focus] */}
+
+ );
+}
+```
+
+## Sign in with Ethereum
+Visit your local server and click "Sign In with Ethereum"
+::::
\ No newline at end of file
diff --git a/docs/pages/guides/tips/inspect-txn-simulation.mdx b/docs/pages/guides/tips/inspect-txn-simulation.mdx
new file mode 100644
index 0000000..f525ad0
--- /dev/null
+++ b/docs/pages/guides/tips/inspect-txn-simulation.mdx
@@ -0,0 +1,11 @@
+# Transaction Simulation Data
+There is a hidden feature which enables you to easily copy transaction simulation request and response data which can then be pasted it in a text editor to inspect.
+
+## Instructions
+- Click the area defined in red **_five times_**, then paste the copied data in a text editor.
+
+
+
+ 
+
+
\ No newline at end of file
diff --git a/docs/pages/guides/tips/popup-tips.mdx b/docs/pages/guides/tips/popup-tips.mdx
new file mode 100644
index 0000000..1c7c83d
--- /dev/null
+++ b/docs/pages/guides/tips/popup-tips.mdx
@@ -0,0 +1,34 @@
+# Popup Tips
+
+## Overview
+When a Smart Wallet is connected and Coinbase Wallet SDK receives a request, it opens
+[keys.coinbase.com](https://keys.coinbase.com/) in a popup window and passes the request to the popup for handling.
+Keep the following points in mind when working with the Smart Wallet popup.
+
+## Default blocking behavior
+- Most modern browsers block all popups by default, unless they are triggered by a click.
+- If a popup is blocked the browser shows a notification to the user, allowing them to manage popup settings.
+
+### What to do about it
+- Ensure there is no additional logic between the button click and the request to open the Smart Wallet popup,
+as browsers might perceive the request as programmatically initiated.
+- If logic is unavoidable, keep it minimal and test thoroughly in all supported browsers.
+
+## `Cross-Origin-Opener-Policy`
+If the Smart Wallet popup opens and displays an error or infinite spinner, it may be due to the dapp's `Cross-Origin-Opener-Policy`. Be sure to use a directive that allows the Smart Wallet popup to function.
+
+- ✅ Allows Smart Wallet popup to function
+ - `unsafe-none` (default)
+ - `same-origin-allow-popups` (recommended)
+- ❌ Breaks Smart Wallet popup
+ - `same-origin`
+
+For more detailed information refer to the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy).
+
+
+## Smart Wallet popup 'linger' behavior
+- Sometimes a dapp may programmatically make a followup request based on the response to a previous request.
+Normally, browsers block these programmatic requests to open popups.
+- To address this, after the Smart Wallet popup responds to a request, it will linger for 200ms to listen for another incoming request before closing.
+ - If a request is received *during* this 200ms window, it will be received and handled within the same popup window.
+ - If a request is received *after* the 200ms window and the popup has closed, opening the Smart Wallet popup will be blocked by the browser.
\ No newline at end of file
diff --git a/docs/pages/guides/update-existing-app.mdx b/docs/pages/guides/update-existing-app.mdx
new file mode 100644
index 0000000..70fda53
--- /dev/null
+++ b/docs/pages/guides/update-existing-app.mdx
@@ -0,0 +1,7 @@
+# Update an Existing Project
+
+## Direct dependency
+Follow the [installation instructions](/sdk/install).
+
+## Wagmi
+1. Upgrade Wagmi to version `2.9.5` or higher.
\ No newline at end of file
diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx
new file mode 100644
index 0000000..2940343
--- /dev/null
+++ b/docs/pages/index.mdx
@@ -0,0 +1,9 @@
+import { useEffect } from 'react';
+
+export default function Redirect() {
+ useEffect(() => {
+ window.location.href = "/why";
+ }, []);
+
+ return null;
+}
\ No newline at end of file
diff --git a/docs/pages/quick-start.mdx b/docs/pages/quick-start.mdx
new file mode 100644
index 0000000..8aef1d7
--- /dev/null
+++ b/docs/pages/quick-start.mdx
@@ -0,0 +1,7 @@
+# Quick Start
+You can try Smart Wallet today on [any supported chain](/FAQ#what-networks-are-supported)! Checkout our guides
+- [Create a new app using Build Onchain Template](/guides/create-app/using-boat)
+- [Create a new app using Wagmi Template](/guides/create-app/using-wagmi)
+- [Update an existing app](/guides/update-existing-app)
+
+Reach out to us in the `#smart-wallet` channel on [Discord](https://discord.com/invite/cdp/) if you have any questions.
\ No newline at end of file
diff --git a/docs/pages/sdk/install.mdx b/docs/pages/sdk/install.mdx
new file mode 100644
index 0000000..3e4707c
--- /dev/null
+++ b/docs/pages/sdk/install.mdx
@@ -0,0 +1,22 @@
+# Install
+
+## Command
+:::code-group
+
+```bash [npm]
+npm i @coinbase/wallet-sdk
+```
+
+```bash [pnpm]
+pnpm i @coinbase/wallet-sdk
+```
+
+```bash [yarn]
+yarn add @coinbase/wallet-sdk
+```
+
+```bash [bun]
+bun i @coinbase/wallet-sdk
+```
+:::
+
diff --git a/docs/pages/sdk/makeWeb3Provider.mdx b/docs/pages/sdk/makeWeb3Provider.mdx
new file mode 100644
index 0000000..d8decab
--- /dev/null
+++ b/docs/pages/sdk/makeWeb3Provider.mdx
@@ -0,0 +1,49 @@
+# makeWeb3Provider
+Creates a new `CoinbaseWalletProvider` instance using a `CoinbaseWalletSDK` instance.
+
+## Usage
+:::code-group
+```ts twoslash [provider.ts]
+import {sdk} from "./setup";
+
+// Create provider
+const provider = sdk.makeWeb3Provider({options: 'smartWalletOnly'});
+// Use provider
+const addresses = provider.request({method: 'eth_requestAccounts'});
+```
+```ts twoslash [setup.ts] filename="setup.ts"
+import { CoinbaseWalletSDK } from '@coinbase/wallet-sdk'
+
+export const sdk = new CoinbaseWalletSDK({
+ appName: 'My App Name',
+ appChainIds: [8453]
+});
+```
+:::
+
+## Returns
+A new `CoinbaseWalletProvider` instance, which is an [Ethereum Javascript provider](https://eips.ethereum.org/EIPS/eip-1193) provider.
+
+## Parameters
+
+### options (optional)
+- Type: `'all' | 'smartWalletOnly' | 'eoaOnly'`
+
+Determines which connection options users will see. Defaults to `all`.
+
+
+#### `all`
+Users will see Smart Wallet and mobile app connection options.
+
+:::info
+If a user is using a browser with Coinbase Wallet Extension installed, they will be taken straight to the
+Coinbase Wallet Extension and not see any choice.
+:::
+
+#### `smartWalletOnly`
+With this option, users will only see an option to create a Smart Wallet or sign into their Smart Wallet.
+
+#### `eoaOnly`
+Users will only see the mobile app connection option. If the user is on mobile, they will be taken directly
+to the Coinbase Wallet app. If the user is using a browser with Coinbase Wallet Extension, they will be taken
+directly to the Extension.
\ No newline at end of file
diff --git a/docs/pages/sdk/setup.mdx b/docs/pages/sdk/setup.mdx
new file mode 100644
index 0000000..1353101
--- /dev/null
+++ b/docs/pages/sdk/setup.mdx
@@ -0,0 +1,30 @@
+# Setup
+Create a new `CoinbaseWalletSDK` instance.
+
+```ts twoslash
+import { CoinbaseWalletSDK } from '@coinbase/wallet-sdk'
+
+const sdk = new CoinbaseWalletSDK({
+ appName: 'My App Name',
+ appChainIds: [8453]
+});
+```
+
+## Parameters
+### appName (optional)
+- Type: `string`
+
+The app name. This will be displayed to users on connection, transacting, and signing requests.
+
+### appChainIds (optional)
+- Type: `number[]`
+
+Array of chain IDs your app supports. Default value is `[1]`.
+
+[What networks are supported?](/FAQ#what-networks-are-supported)
+
+### appLogoUrl (optional)
+- Type: `string`
+
+App logo image URL. Favicon is used if unspecified.
+
diff --git a/docs/pages/sdk/v3-to-v4-changes.mdx b/docs/pages/sdk/v3-to-v4-changes.mdx
new file mode 100644
index 0000000..c2c5b4f
--- /dev/null
+++ b/docs/pages/sdk/v3-to-v4-changes.mdx
@@ -0,0 +1,127 @@
+# CoinbaseWalletSDK Changes (v3 to v4)
+
+This page details changes between versions 3.x and 4.x of the [Coinbase Wallet SDK](https://github.com/coinbase/coinbase-wallet-sdk).
+
+## CoinbaseWalletSDK
+
+### CoinbaseWalletSDKOptions
+
+```ts
+// v4
+type CoinbaseWalletSDKOptions = {
+ appName?: string | undefined;
+ appLogoUrl?: string | null | undefined;
+ appChainIds?: number[] | undefined;
+};
+```
+
+#### New (v4 only):
+
+- `appChainIds?: number[]`
+ - An array of chain IDs your dapp supports
+ - The first chain in this array will be used as the default chain.
+ - Removes the need for non-mainnet dapps to request switching chains before making first request.
+ - Default value is `[1]` (mainnet)
+
+#### Deprecated (v3 only):
+
+- `enableMobileWalletLink` (enabled by default in v4)
+- `jsonRpcUrl`
+- `reloadOnDisconnect`
+- `uiConstructor`
+- `overrideIsMetaMask`
+- `overrideIsCoinbaseWallet`
+- `diagnosticLogger`
+- `reloadOnDisconnect`
+- `headlessMode`
+
+### Deprecated functions
+- `CoinbaseWalletSDK.disconnect()` is deprecated
+ - dapps should call `CoinbaseWalletProvider.disconnect()` instead
+- `CoinbaseWalletSDK.setAppInfo()` is deprecated
+ - Dapps should pass in `appName` and `appLogoUrl` via `CoinbaseWalletSDKOptions`
+
+### `makeWeb3Provider`
+
+#### Signature
+ ```ts
+ // v3
+ makeWeb3Provider(jsonRpcUrl?: string, chainId?: number): CoinbaseWalletProvider
+
+ // v4 // [!code focus]
+ makeWeb3Provider(preference: Preference = { options: 'all' }): ProviderInterface // [!code focus]
+ ```
+#### Parameters
+ ```ts
+ interface Preference {
+ options: 'all' | 'smartWalletOnly' | 'eoaOnly';
+ }
+ ```
+ - `options`
+ - `'all'` (default) show both smart wallet and EOA options
+ - `'smartWalletOnly'` only show smart wallet option
+ - `'eoaOnly'` only show EOA option
+
+#### Return type
+ ```ts
+ export interface ProviderInterface extends EventEmitter {
+ request(args: RequestArguments): Promise;
+ disconnect(): Promise;
+ on(event: 'connect', listener: (info: ProviderConnectInfo) => void): this;
+ on(event: 'disconnect', listener: (error: ProviderRpcError) => void): this;
+ on(event: 'chainChanged', listener: (chainId: string) => void): this;
+ on(event: 'accountsChanged', listener: (accounts: string[]) => void): this;
+ on(event: 'message', listener: (message: ProviderMessage) => void): this;
+ }
+ ```
+
+## CoinbaseWalletProvider
+
+### `connect` event fix
+- v3 returned `chainIdStr` instead of `chainId`.
+- v4 is [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193#connect) compliant.
+
+```ts
+// v4
+interface ProviderConnectInfo { readonly chainId: string; }
+on(event: 'connect', listener: (info: ProviderConnectInfo) => void): this;
+```
+
+### `eth_accounts` when disconnected
+- v3 returned an empty array.
+- v4 throws an error.
+ - `Error: Must call 'eth_requestAccounts' before other methods`
+
+
+
+### Deprecated functionality
+
+#### Instance properties
+
+- `isCoinbaseBrowser: boolean`
+- `qrUrl?: string | null`
+- `reloadOnDisconnect: boolean`
+
+#### Getter methods
+
+- `selectedAddress`
+- `networkVersion`
+- `isWalletLink`
+- `ismetaMask`
+- `host`
+
+#### Methods
+
+- `disableReloadOnDisconnect`
+- `setProviderInfo`
+- `setAppInfo`
+- `close`
+- `send`
+- `sendAsync`
+- `scanQRCode`
+- `genericRequest`
+- `connectAndSignIn`
+- `selectProvider`
+- `supportsSubscriptions`
+- `subscribe`
+- `unsubscribe`
diff --git a/docs/pages/wallet-library-support.mdx b/docs/pages/wallet-library-support.mdx
new file mode 100644
index 0000000..1aa31a4
--- /dev/null
+++ b/docs/pages/wallet-library-support.mdx
@@ -0,0 +1,12 @@
+# Wallet Library Support
+Below are some popular wallet libraries and what we know of their plans for day 1 support for Smart Wallet.
+
+| Name | Support |
+|------|---------|
+| [Dynamic](https://docs.dynamic.xyz/wallets/advanced-wallets/coinbase-smart-wallet) | ✅ |
+| [Privy]( https://docs.privy.io/guide/react/recipes/misc/coinbase-smart-wallets) | ✅ |
+| [ThirdWeb](http://portal.thirdweb.com/connect) | ✅ |
+| [ConnectKit](https://docs.family.co/connectkit) | ✅ |
+| [Web3Modal](https://docs.walletconnect.com/web3modal/react/smart-accounts) | ✅ |
+| [Web3-Onboard](https://www.blocknative.com/coinbase-wallet-integration) | ✅ |
+| [RainbowKit](https://www.rainbowkit.com/) | ✅ |
diff --git a/docs/pages/why.mdx b/docs/pages/why.mdx
new file mode 100644
index 0000000..d3d0260
--- /dev/null
+++ b/docs/pages/why.mdx
@@ -0,0 +1,29 @@
+# Why Smart Wallet?
+
+## Instant Onboarding
+Smart Wallet enables users to create an account in seconds with no app or extension required.
+
+This is possible because Smart Wallet uses passkeys for signing.
+Passkeys are generated and stored securely on users' devices.
+If the user has iCloud or Google Password Manager, their passkeys will also be
+backed up and synced across devices [1](https://developer.apple.com/passkeys/), [2](https://developers.google.com/identity/passkeys/supported-environments).
+
+Note this is *not* passkey authentication to some server where we store a users key:
+users' on device passkey is what is [validated for signing onchain](https://github.com/base-org/webauthn-sol).
+
+## Zero Cost
+Smart Wallet is free for both developers and users.
+
+## Unified Account Across Apps
+Smart Wallet users can use the same account and address across all onchain apps.
+
+## Magic Spend
+Smart Wallet users can connect their coinbase.com account and spend their coinbase.com held ETH from
+their Smart Account (Base only, for now).
+
+## Batch Transactions, Paymasters, and more!
+Our Smart Wallet is ERC-4337 compliant and our SDK supports batch transactions and app-defined paymasters.
+
+## Coinbase Security
+Users will see transaction and signing previews that will help keep them safe.
+Our smart contracts have been audited 4 times by leading audit teams.
diff --git a/docs/public/buttonPlacementExamples.png b/docs/public/buttonPlacementExamples.png
new file mode 100644
index 0000000..e871614
Binary files /dev/null and b/docs/public/buttonPlacementExamples.png differ
diff --git a/docs/public/copyTxnSimClickZone.png b/docs/public/copyTxnSimClickZone.png
new file mode 100644
index 0000000..02bbe52
Binary files /dev/null and b/docs/public/copyTxnSimClickZone.png differ
diff --git a/docs/public/createWalletButtonBlack.png b/docs/public/createWalletButtonBlack.png
new file mode 100644
index 0000000..cdbf011
Binary files /dev/null and b/docs/public/createWalletButtonBlack.png differ
diff --git a/docs/public/createWalletButtonBlue.png b/docs/public/createWalletButtonBlue.png
new file mode 100644
index 0000000..893a5fb
Binary files /dev/null and b/docs/public/createWalletButtonBlue.png differ
diff --git a/docs/public/examplePlacement.png b/docs/public/examplePlacement.png
new file mode 100644
index 0000000..c2cf5a4
Binary files /dev/null and b/docs/public/examplePlacement.png differ
diff --git a/docs/public/logo.svg b/docs/public/logo.svg
new file mode 100644
index 0000000..aa2c382
--- /dev/null
+++ b/docs/public/logo.svg
@@ -0,0 +1,4 @@
+
diff --git a/docs/styles.css b/docs/styles.css
new file mode 100644
index 0000000..16a9ae7
--- /dev/null
+++ b/docs/styles.css
@@ -0,0 +1,13 @@
+.vocs_NavLogo_title {
+ color: #0d59fe;
+ font-size: 2.5em;
+ font-weight: bold;
+}
+
+.vocs_Sidebar_sectionTitle {
+ color: #0d59fe;
+}
+
+.vocs_Body {
+ background-color: yellow;
+}
\ No newline at end of file
diff --git a/dprint.json b/dprint.json
new file mode 100644
index 0000000..4220195
--- /dev/null
+++ b/dprint.json
@@ -0,0 +1,17 @@
+{
+ "json": {
+ },
+ "markdown": {
+ },
+ "typescript": {
+ },
+ "excludes": [
+ "**/*-lock.json",
+ "/docs/dist/**"
+ ],
+ "plugins": [
+ "https://plugins.dprint.dev/json-0.19.2.wasm",
+ "https://plugins.dprint.dev/markdown-0.16.4.wasm",
+ "https://plugins.dprint.dev/typescript-0.90.4.wasm"
+ ]
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..cb43437
--- /dev/null
+++ b/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "coinbase-smart-wallet-documentation",
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vocs dev",
+ "build": "vocs build",
+ "preview": "vocs preview",
+ "format": "dprint fmt",
+ "prepare": "bun x simple-git-hooks"
+ },
+ "dependencies": {
+ "@types/react": "latest",
+ "@vercel/analytics": "^1.2.2",
+ "react": "latest",
+ "react-dom": "latest",
+ "typescript": "latest",
+ "vocs": "latest"
+ },
+ "devDependencies": {
+ "dprint": "^0.45.1",
+ "permissionless": "^0.1.29",
+ "simple-git-hooks": "^2.11.1",
+ "siwe": "^2.3.2",
+ "viem": "^2.9.28",
+ "wagmi": "^2.9.0"
+ },
+ "simple-git-hooks": {
+ "pre-commit": "bun dprint check"
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..d2636aa
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["**/*.ts", "**/*.tsx"]
+}
diff --git a/vocs.config.tsx b/vocs.config.tsx
new file mode 100644
index 0000000..163d917
--- /dev/null
+++ b/vocs.config.tsx
@@ -0,0 +1,141 @@
+import { defineConfig } from "vocs";
+
+export default defineConfig({
+ head() {
+ return ;
+ },
+ theme: {
+ colorScheme: "system",
+ variables: {
+ color: {
+ textAccent: { light: "#2394ff", dark: "#e9e9ea" },
+ backgroundDark: { light: "#ebeaee", dark: "black" },
+ background: { light: "#f6f5f8", dark: "black" }, // f6f5f8 /ebf1f8
+ heading: { light: "black", dark: "#e9e9ea" },
+ text3: { light: "black", dark: "#e9e9ea" },
+ background5: { light: "#dee8ff", dark: "#3c393f" },
+ },
+ },
+ },
+ ogImageUrl: "https://vocs.dev/api/og?logo=%logo&title=%title&description=%description",
+ title: "Smart Wallet",
+ description: "Smart Wallet Documentation",
+ // logoUrl: '/logo.svg',
+ iconUrl: "/logo.svg",
+ topNav: [
+ { text: "Demo", link: "https://smart-wallet.xyz/" },
+ { text: "SDK Playground", link: "https://coinbase.github.io/coinbase-wallet-sdk/" },
+ { text: "Github", link: "https://github.com/coinbase/coinbase-wallet-sdk" },
+ { text: "Discord", link: "https://discord.com/invite/cdp/" },
+ ],
+ sidebar: [
+ {
+ text: "Why Smart Wallet?",
+ link: "/why",
+ },
+ {
+ text: "Quick Start",
+ link: "/quick-start",
+ },
+ {
+ text: "FAQ",
+ link: "/FAQ",
+ },
+ {
+ text: "Launch Ready Checklist",
+ link: "/checklist",
+ },
+ {
+ text: "Wallet Library Support",
+ link: "/wallet-library-support",
+ },
+ {
+ text: "Base Gasless Campaign",
+ link: "/base-gasless-campaign",
+ },
+ {
+ text: "Guides",
+ collapsed: false,
+ items: [
+ {
+ text: "Create a New App",
+ items: [
+ { text: "Using Build Onchain Template", link: "/guides/create-app/using-boat" },
+ { text: "Using Wagmi Template", link: "/guides/create-app/using-wagmi" },
+ ],
+ },
+ {
+ text: "Update an Existing App",
+ link: "/guides/update-existing-app",
+ },
+ {
+ text: "Create Wallet Button",
+ link: "/guides/components/create-wallet-button",
+ },
+ {
+ text: "Signature Verification",
+ link: "/guides/signature-verification",
+ },
+ {
+ text: "Sign-In with Ethereum",
+ link: "/guides/siwe",
+ },
+ {
+ text: "Batch Transactions",
+ link: "/guides/batch-transactions",
+ },
+ {
+ text: "Paymasters (Sponsored Transactions)",
+ link: "/guides/paymasters",
+ },
+ {
+ text: "Magic Spend Support",
+ link: "/guides/magic-spend",
+ },
+ {
+ text: "Tips & Tricks",
+ items: [
+ {
+ text: "Inspect Transaction Simulation",
+ link: "/guides/tips/inspect-txn-simulation",
+ },
+ {
+ text: "Popup Tips",
+ link: "/guides/tips/popup-tips",
+ },
+ ],
+ },
+ ],
+ },
+ {
+ text: "SDK",
+ collapsed: false,
+ items: [
+ {
+ text: "Install",
+ link: "/sdk/install",
+ },
+ {
+ text: "Setup",
+ link: "/sdk/setup",
+ },
+ {
+ text: "makeWeb3Provider",
+ link: "/sdk/makeWeb3Provider",
+ },
+ {
+ text: "Upgrading from 3.x",
+ link: "/sdk/v3-to-v4-changes",
+ },
+ ],
+ },
+ {
+ text: "Smart Contracts",
+ collapsed: false,
+ items: [
+ { text: "Smart Wallet", link: "https://github.com/coinbase/smart-wallet" },
+ { text: "Magic Spend", link: "https://github.com/coinbase/magic-spend" },
+ ],
+ },
+ ],
+});