diff --git a/.gitignore b/.gitignore index e36586d..b113dd8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules /docs/dist +/generated \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 7404e8a..492f1d6 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/docs/components/sandpack/RequestAccountsHTML.tsx b/docs/components/sandpack/RequestAccountsHTML.tsx new file mode 100644 index 0000000..1899f70 --- /dev/null +++ b/docs/components/sandpack/RequestAccountsHTML.tsx @@ -0,0 +1,14 @@ +import { Sandpack } from "@codesandbox/sandpack-react"; +import { staticRequestAccounts, staticRequestAccountsScript } from "../../../generated/sandpackFiles"; + +export default function RequestAccountsHTML() { + return ( + + ); +} diff --git a/docs/components/sandpack/RequestAccountsReact.tsx b/docs/components/sandpack/RequestAccountsReact.tsx new file mode 100644 index 0000000..bb3fd6e --- /dev/null +++ b/docs/components/sandpack/RequestAccountsReact.tsx @@ -0,0 +1,19 @@ +import { Sandpack } from "@codesandbox/sandpack-react"; +import { reactConfig, reactRequestAccounts } from "../../../generated/sandpackFiles"; + +export default function RequestAccountsReact() { + return ( + + ); +} diff --git a/docs/components/sandpack/files/react/RequestAccounts.jsx b/docs/components/sandpack/files/react/RequestAccounts.jsx new file mode 100644 index 0000000..2e02e79 --- /dev/null +++ b/docs/components/sandpack/files/react/RequestAccounts.jsx @@ -0,0 +1,25 @@ +import { useState } from "react"; +import { sdk } from "./config"; + +export default function App() { + const [address, setAddress] = useState(undefined); + const provider = sdk.makeWeb3Provider({ options: "smartWalletOnly" }); + + const handleClick = async () => { + const accounts = await provider.request({ method: "eth_requestAccounts" }); + setAddress(accounts[0]); + }; + + return ( + <> + {!address && ( + + )} + {address &&

Connected address: {address}

} + + ); +} diff --git a/docs/components/sandpack/files/react/config.js b/docs/components/sandpack/files/react/config.js new file mode 100644 index 0000000..c11ac11 --- /dev/null +++ b/docs/components/sandpack/files/react/config.js @@ -0,0 +1,8 @@ +import { CoinbaseWalletSDK } from "@coinbase/wallet-sdk"; + +const baseSepoliaChainId = 84532; + +export const sdk = new CoinbaseWalletSDK({ + appName: "My App Name", + appChainIds: [baseSepoliaChainId], +}); diff --git a/docs/components/sandpack/files/static/requestAccounts.html b/docs/components/sandpack/files/static/requestAccounts.html new file mode 100644 index 0000000..64d0435 --- /dev/null +++ b/docs/components/sandpack/files/static/requestAccounts.html @@ -0,0 +1,17 @@ + + + + + + Connect to Coinbase Wallet + + + +
+ + +
+ + diff --git a/docs/components/sandpack/files/static/requestAccountsScript.js b/docs/components/sandpack/files/static/requestAccountsScript.js new file mode 100644 index 0000000..59e9838 --- /dev/null +++ b/docs/components/sandpack/files/static/requestAccountsScript.js @@ -0,0 +1,32 @@ +import CoinbaseWalletSDK from "https://esm.sh/@coinbase/wallet-sdk"; + +document.addEventListener("DOMContentLoaded", () => { + const baseSepoliaChainId = 84532; + + const coinbaseWallet = new CoinbaseWalletSDK({ + appName: "My App Name", + appChainIds: [baseSepoliaChainId], // Replace with your chain IDs if needed + }); + + const provider = coinbaseWallet.makeWeb3Provider({ options: "smartWalletOnly" }); + + const connectButton = document.getElementById("connectButton"); + const connectedAddressParagraph = document.getElementById("connectedAddress"); + const addressSpan = document.getElementById("address"); + + // Event listener for the connect button + connectButton.addEventListener("click", async () => { + try { + // Request accounts from the provider + const accounts = await provider.request({ method: "eth_requestAccounts" }); + if (accounts.length > 0) { + // Update the UI to show the connected address + addressSpan.textContent = accounts[0]; + connectedAddressParagraph.style.display = "block"; + connectButton.style.display = "none"; + } + } catch (error) { + console.error("Error connecting to Coinbase Wallet:", error); + } + }); +}); diff --git a/docs/pages/sdk/CoinbaseWalletProvider/disconnect.mdx b/docs/pages/sdk/CoinbaseWalletProvider/disconnect.mdx new file mode 100644 index 0000000..e69de29 diff --git a/docs/pages/sdk/CoinbaseWalletProvider/overview.mdx b/docs/pages/sdk/CoinbaseWalletProvider/overview.mdx new file mode 100644 index 0000000..4748204 --- /dev/null +++ b/docs/pages/sdk/CoinbaseWalletProvider/overview.mdx @@ -0,0 +1,8 @@ +# Overview +## Introduction +CoinbaseWalletProvider is an [JavaScript Ethereum provider](https://eips.ethereum.org/EIPS/eip-1193). +It allows JavaScript applications to make Ethereum RPC requests, via its `request` method. +These requests will be handled in one of three ways +1. Sent to the Wallet (Wallet mobile app, extension, or popup window). +2. Handled locally +3. Passed onto default RPC provider for the given chain, if it exists. diff --git a/docs/pages/sdk/CoinbaseWalletProvider/request/eth_requestAccounts.mdx b/docs/pages/sdk/CoinbaseWalletProvider/request/eth_requestAccounts.mdx new file mode 100644 index 0000000..258ba90 --- /dev/null +++ b/docs/pages/sdk/CoinbaseWalletProvider/request/eth_requestAccounts.mdx @@ -0,0 +1,30 @@ +import RequestAccountsReact from '../../../../components/sandpack/RequestAccountsReact'; +import RequestAccountsHTML from '../../../../components/sandpack/RequestAccountsHTML'; + +# eth_requestAccounts +Defined in [EIP-1102](https://eips.ethereum.org/EIPS/eip-1102) +> Calling this method MAY trigger a user interface that allows the user to approve or reject account access for a given app. + + +## Returns +`Array` + +An array of Ethereum addresses, which the connected user controls. + +## Errors +| Code | Message | +| ---- | ------- | +| 4001 | User denied connection request | + + +## Examples +:::code-group + +
+ +
+ +
+ +
+::: \ No newline at end of file diff --git a/docs/pages/sdk/CoinbaseWalletProvider/request/overview.mdx b/docs/pages/sdk/CoinbaseWalletProvider/request/overview.mdx new file mode 100644 index 0000000..9241a6b --- /dev/null +++ b/docs/pages/sdk/CoinbaseWalletProvider/request/overview.mdx @@ -0,0 +1,86 @@ +# Overview +The `request` method allows apps to make make Ethereum RPC requests to the wallet. + +## Specification + +```ts twoslash +interface RequestArguments { + readonly method: string; + readonly params?: readonly unknown[] | object; +} + +interface ProviderRpcError extends Error { + code: number; + data?: unknown; +} + +interface CoinbaseWalletProvider { + /** + * @param {RequestArguments} args request arguments. + * @returns A promise that resolves with the result. + * @throws {ProviderRpcError} incase of error. + * @fires CoinbaseWalletProvider#connect When the provider successfully connects. + */ + request: (args: RequestArguments) => Promise +} +``` +### Example +:::code-group +```ts twoslash [example.ts] +import {provider} from "./setup"; + +const addresses = await provider.request({method: 'eth_requestAccounts'}); +const txHash = await provider.request({ + method: 'eth_sendTransaction', + params: [{from: addresses[0], to: addresses[0], value: 1}] + } +); +``` + +```ts twoslash [setup.ts] filename="setup.ts" +import { CoinbaseWalletSDK } from '@coinbase/wallet-sdk' + +const baseSepoliaChainId = 84532; + +export const sdk = new CoinbaseWalletSDK({ + appName: 'My App Name', + appChainIds: [baseSepoliaChainId] +}); + +const provider = sdk.makeWeb3Provider(); +``` +::: + +## Request Handling +Requests are hanlded in one of three ways +1. Sent to the Wallet application (Wallet mobile app, extension, or popup window). +2. Handled locally by the SDK. +3. Passed onto default RPC provider for the given chain, if it exists. + +### 1. Sent to the Wallet application +The following RPC requests are sent to the Wallet application: +- eth_ecRecover +- personal_sign +- personal_ecRecover +- eth_signTransaction +- eth_sendTransaction +- eth_signTypedData_v1 +- eth_signTypedData_v3 +- eth_signTypedData_v4 +- eth_signTypedData +- wallet_addEthereumChain +- wallet_watchAsset +- wallet_sendCalls +- wallet_showCallsStatus + +### 2. Handled Locally by the SDK +The following requests are handled locally by the SDK, with no external calls. +- eth_requestAccounts +- eth_accounts +- eth_coinbase +- net_version +- eth_chainId +- wallet_getCapabilities +- wallet_switchEthereumChain + + diff --git a/docs/pages/sdk/install.mdx b/docs/pages/sdk/install.mdx index 3e4707c..f8c4a8d 100644 --- a/docs/pages/sdk/install.mdx +++ b/docs/pages/sdk/install.mdx @@ -19,4 +19,3 @@ yarn add @coinbase/wallet-sdk bun i @coinbase/wallet-sdk ``` ::: - diff --git a/docs/pages/sdk/recommendedTools.mdx b/docs/pages/sdk/recommendedTools.mdx new file mode 100644 index 0000000..e69de29 diff --git a/dprint.json b/dprint.json index 4220195..1766f6b 100644 --- a/dprint.json +++ b/dprint.json @@ -1,6 +1,8 @@ { "json": { }, + "markup": { + }, "markdown": { }, "typescript": { @@ -12,6 +14,7 @@ "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" + "https://plugins.dprint.dev/typescript-0.90.4.wasm", + "https://plugins.dprint.dev/g-plane/markup_fmt-v0.11.0.wasm" ] } diff --git a/generateSandpackFiles.js b/generateSandpackFiles.js new file mode 100644 index 0000000..e0d45b7 --- /dev/null +++ b/generateSandpackFiles.js @@ -0,0 +1,46 @@ +const fs = require("fs"); +const path = require("path"); + +const sandpackDir = path.join(__dirname, "docs", "components", "sandpack", "files"); +const outputFile = path.join(__dirname, "generated", "sandpackFiles.ts"); + +function readDirectoryRecursively(dir) { + const results = {}; + const list = fs.readdirSync(dir); + + list.forEach(file => { + const filePath = path.join(dir, file); + const stat = fs.statSync(filePath); + + if (stat && stat.isDirectory()) { + results[file] = readDirectoryRecursively(filePath); + } else { + results[file] = fs.readFileSync(filePath, "utf8"); + } + }); + + return results; +} + +const sandpackExamples = readDirectoryRecursively(sandpackDir); + +// Generate TypeScript content +let tsContent = `// This file is auto-generated. Do not edit manually.\n\n`; + +Object.entries(sandpackExamples).forEach(([framework, examples]) => { + Object.entries(examples).forEach(([exampleName, files]) => { + const name = exampleName.split(".")[0]; + const varName = `${framework.split(".")[0]}${name.charAt(0).toUpperCase() + name.slice(1)}`; + tsContent += `export const ${varName} = ${JSON.stringify(files, null, 2)};\n\n`; + }); +}); + +// Ensure the output directory exists +const outputDir = path.dirname(outputFile); +if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); +} + +fs.writeFileSync(outputFile, tsContent); + +console.log("Sandpack examples TypeScript file generated successfully!"); diff --git a/package.json b/package.json index cb43437..bfdb96f 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,16 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vocs dev", - "build": "vocs build", + "generate": "bun generateSandpackFiles.js", + "dev": "bun generate && vocs dev", + "build": "bun generate && vocs build", "preview": "vocs preview", "format": "dprint fmt", "prepare": "bun x simple-git-hooks" }, "dependencies": { + "@codesandbox/sandpack-react": "^2.18.0", + "@codesandbox/sandpack-themes": "^2.0.21", "@types/react": "latest", "@vercel/analytics": "^1.2.2", "react": "latest", diff --git a/tsconfig.json b/tsconfig.json index d2636aa..ca099d0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,5 +20,11 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, - "include": ["**/*.ts", "**/*.tsx"] + "include": [ + "**/*.ts", + "**/*.tsx", + "generateSandpackFiles.js", + "docs/components/sandpack/files/react/config.js", + "docs/components/sandpack/files/react/RequestAccounts.jsx" + ] } diff --git a/vocs.config.tsx b/vocs.config.tsx index 163d917..a5f6b23 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -127,6 +127,19 @@ export default defineConfig({ text: "Upgrading from 3.x", link: "/sdk/v3-to-v4-changes", }, + { + text: "CoinbaseWalletProvider", + items: [ + { text: "Overview", link: "/sdk/CoinbaseWalletProvider/overview" }, + { + text: "request", + items: [ + { text: "Overview", link: "/sdk/CoinbaseWalletProvider/request/overview" }, + { text: "eth_requestAccounts", link: "/sdk/CoinbaseWalletProvider/request/eth_requestAccounts" }, + ], + }, + ], + }, ], }, {