From 6b0443b3e1c535bf4d8c2ec25909661bddf73448 Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Thu, 7 Nov 2024 11:07:45 -0800 Subject: [PATCH 01/10] more detailed provider configuration codeblock --- .../guides/spend-permissions/quick-start.mdx | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/pages/guides/spend-permissions/quick-start.mdx b/docs/pages/guides/spend-permissions/quick-start.mdx index a9eb563..938513b 100644 --- a/docs/pages/guides/spend-permissions/quick-start.mdx +++ b/docs/pages/guides/spend-permissions/quick-start.mdx @@ -170,9 +170,33 @@ export async function getSpenderBundlerClient() { ### Configure the Smart Wallet URL -In `providers.tsx`, update the value of `keysUrl` to be `"https://wallet.chameleon.systems"`. +In `providers.tsx`, update the value of `keysUrl` to be `"https://keys-beta.coinbase.com/connect"`. This will point your app to connect to our public dev environment for smart wallet connections. +Your config should now look like this: + +``` +const config = createConfig({ + chains: [baseSepolia], + connectors: [ + coinbaseWallet({ + appName: process.env.NEXT_PUBLIC_ONCHAINKIT_PROJECT_NAME, + preference: process.env.NEXT_PUBLIC_ONCHAINKIT_WALLET_CONFIG as + | "smartWalletOnly" + | "all", + // @ts-ignore + keysUrl: "https://keys-beta.coinbase.com/connect" + }), + ], + storage: createStorage({ + storage: cookieStorage, + }), + ssr: true, + transports: { + [baseSepolia.id]: http(), + }, +}); +``` ### Set up our interface to the `SpendPermissionManager` smart contract From 6ebe6476a019df258218866dcb773c13683069d1 Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Thu, 7 Nov 2024 11:20:01 -0800 Subject: [PATCH 02/10] update name of spender wallet env variables --- .../guides/spend-permissions/quick-start.mdx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/pages/guides/spend-permissions/quick-start.mdx b/docs/pages/guides/spend-permissions/quick-start.mdx index 938513b..ed2a041 100644 --- a/docs/pages/guides/spend-permissions/quick-start.mdx +++ b/docs/pages/guides/spend-permissions/quick-start.mdx @@ -35,18 +35,18 @@ We insecurely use an environment variable in this demo for simplicity. Create a local `.env` file and add these environment variables: ``` -SUBSCRIPTION_PRIVATE_KEY= -NEXT_PUBLIC_SUBSCRIPTION_SPENDER= +SPENDER_OWNER_PRIVATE_KEY= +NEXT_PUBLIC_SPENDER_WALLET_ADDRESS= NEXT_PUBLIC_CDP_API_KEY= BASE_SEPOLIA_PAYMASTER_URL= ``` -The `SUBSCRIPTION_PRIVATE_KEY` will be our spender private key and `NEXT_PUBLIC_SUBSCRIPTION_SPENDER` will be the address of our spender smart contract wallet. +The `SPENDER_OWNER_PRIVATE_KEY` will be our spender private key and `NEXT_PUBLIC_SPENDER_WALLET_ADDRESS` will be the address of our spender smart contract wallet. You can use a development private key you already have, or generate a random new one. If you have [Foundry](https://book.getfoundry.sh/) installed, you can generate a new wallet via `cast wallet new`. This wallet won't need to hold any ETH for gas because our spender smart contract wallet will be sponsored by a paymaster. -Paste your private key as the value for `SUBSCRIPTION_PRIVATE_KEY`. This should be hex-prefixed, i.e. `SUBSCRIPTION_PRIVATE_KEY=0xAbC123...dEf456` +Paste your private key as the value for `SPENDER_OWNER_PRIVATE_KEY`. This should be hex-prefixed, i.e. `SPENDER_OWNER_PRIVATE_KEY=0xAbC123...dEf456` This private key is an EOA, but we want our spender to be a smart contract wallet. We'll achieve this by treating our new EOA as the owner of a brand new [Coinbase Smart Wallet](/why.mdx). The address of a smart contract wallet is deterministic, and depends on the bytecode of the implementation @@ -76,7 +76,7 @@ export async function logSpenderSmartContractWalletAddress() { }); const spenderAccountOwner = privateKeyToAccount( - process.env.NEXT_PUBLIC_SUBSCRIPTION_PRIVATE_KEY! as Hex + process.env.NEXT_PUBLIC_SPENDER_OWNER_PRIVATE_KEY! as Hex ); console.log("spenderAccountOwner", spenderAccountOwner.address); @@ -100,7 +100,7 @@ if (require.main === module) { ``` Run this script to log the counterfactual address of your new smart contract wallet and assign this address to the -`NEXT_PUBLIC_SUBSCRIPTION_SPENDER` variable in your .env: +`NEXT_PUBLIC_SPENDER_WALLET_ADDRESS` variable in your .env: ```bash npm install -g ts-node typescript @types/node dotenv && ts-node logSpenderSmartContractWalletAddress.ts @@ -145,7 +145,7 @@ export async function getSpenderBundlerClient() { }); const spenderAccountOwner = privateKeyToAccount( - process.env.SUBSCRIPTION_PRIVATE_KEY! as Hex + process.env.SPENDER_OWNER_PRIVATE_KEY! as Hex ); const spenderAccount = await toCoinbaseSmartAccount({ @@ -271,7 +271,7 @@ export default function Subscribe() { const spendPermission = { account: accountAddress, // User wallet address - spender: process.env.NEXT_PUBLIC_SUBSCRIPTION_SPENDER! as Address, // Spender smart contract wallet address + spender: process.env.NEXT_PUBLIC_SPENDER_WALLET_ADDRESS! as Address, // Spender smart contract wallet address token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" as Address, // ETH (https://eips.ethereum.org/EIPS/eip-7528) allowance: parseUnits("10", 18), period: 86400, // seconds in a day @@ -469,7 +469,7 @@ export default function Subscribe() { // Define a `SpendPermission` to request from the user // [!code focus] const spendPermission = { // [!code focus] account: accountAddress, // User wallet address // [!code focus] - spender: process.env.NEXT_PUBLIC_SUBSCRIPTION_SPENDER! as Address, // Spender smart contract wallet address // [!code focus] + spender: process.env.NEXT_PUBLIC_SPENDER_WALLET_ADDRESS! as Address, // Spender smart contract wallet address // [!code focus] token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" as Address, // ETH (https://eips.ethereum.org/EIPS/eip-7528) // [!code focus] allowance: parseUnits("10", 18), // [!code focus] period: 86400, // seconds // [!code focus] From a83f92c2e7d248bbf0498741ff8be6a4c30f270c Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Thu, 7 Nov 2024 14:32:31 -0800 Subject: [PATCH 03/10] more buildbash feedback --- .../guides/spend-permissions/quick-start.mdx | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/docs/pages/guides/spend-permissions/quick-start.mdx b/docs/pages/guides/spend-permissions/quick-start.mdx index ed2a041..fc952aa 100644 --- a/docs/pages/guides/spend-permissions/quick-start.mdx +++ b/docs/pages/guides/spend-permissions/quick-start.mdx @@ -27,12 +27,17 @@ From here, we'll modify the app to assemble, sign, approve and use a spend permi ### Set up your spender wallet - -Always secure your private keys appropriately! -We insecurely use an environment variable in this demo for simplicity. - +In this example, our app spender wallet will be a brand new Coinbase Smart Wallet. This means the wallet itself is a smart +contract, and this smart contract has at least one owner that can produce signatures on behalf of the wallet. +In our case, this owner will be an EOA. Our app will need access to this owner EOA private key, to generate +signatures, as well as the address of the actual smart contract wallet, which is the spender address actually +being authorized by the spend permission. -Create a local `.env` file and add these environment variables: +The address of a smart contract wallet is deterministic, and depends on the bytecode of the implementation +contract combined with a salt, which in our case will be an array consisting of the initial owner(s) of the smart contract wallet. +See the [smart wallet repository](https://github.com/coinbase/smart-wallet) for more details about how this works. + +Add these environment variables to your `.env` file: ``` SPENDER_OWNER_PRIVATE_KEY= @@ -43,20 +48,19 @@ BASE_SEPOLIA_PAYMASTER_URL= The `SPENDER_OWNER_PRIVATE_KEY` will be our spender private key and `NEXT_PUBLIC_SPENDER_WALLET_ADDRESS` will be the address of our spender smart contract wallet. + +Always secure your private keys appropriately! +We insecurely use an environment variable in this demo for simplicity. + + You can use a development private key you already have, or generate a random new one. If you have [Foundry](https://book.getfoundry.sh/) installed, you can generate a new wallet via `cast wallet new`. This wallet won't need to hold any ETH for gas because our spender smart contract wallet will be sponsored by a paymaster. Paste your private key as the value for `SPENDER_OWNER_PRIVATE_KEY`. This should be hex-prefixed, i.e. `SPENDER_OWNER_PRIVATE_KEY=0xAbC123...dEf456` -This private key is an EOA, but we want our spender to be a smart contract wallet. We'll achieve this by treating our new EOA as the owner of a -brand new [Coinbase Smart Wallet](/why.mdx). The address of a smart contract wallet is deterministic, and depends on the bytecode of the implementation -contract combined with a salt, which in our case will be an array consisting of the initial owner(s) of the smart contract wallet. See the -[smart wallet repository](https://github.com/coinbase/smart-wallet) for more details. +We'll have to generate the deterministic address of the Smart Wallet owned by this private key so we can store it as a public environment variable. -We'll generate the deterministic address of this Smart Wallet -so we can store it as a public environment variable. - -Paste the following code into a top-level file called `logSpenderSmartWalletAddress.ts` +Paste the following code into a file at the project root called `logSpenderSmartWalletAddress.ts` ```ts [logSpenderSmartWalletAddress.tsx] import { createPublicClient, Hex, http } from "viem"; @@ -69,14 +73,14 @@ import dotenv from "dotenv"; dotenv.config(); -export async function logSpenderSmartContractWalletAddress() { +export async function logSpenderSmartWalletAddress() { const client = createPublicClient({ chain: baseSepolia, transport: http(), }); const spenderAccountOwner = privateKeyToAccount( - process.env.NEXT_PUBLIC_SPENDER_OWNER_PRIVATE_KEY! as Hex + process.env.SPENDER_OWNER_PRIVATE_KEY! as Hex ); console.log("spenderAccountOwner", spenderAccountOwner.address); @@ -88,7 +92,7 @@ console.log("Spender Smart Wallet Address:", spenderAccount.address); } async function main() { - await logSpenderSmartContractWalletAddress(); + await logSpenderSmartWalletAddress(); } if (require.main === module) { @@ -103,7 +107,7 @@ Run this script to log the counterfactual address of your new smart contract wal `NEXT_PUBLIC_SPENDER_WALLET_ADDRESS` variable in your .env: ```bash -npm install -g ts-node typescript @types/node dotenv && ts-node logSpenderSmartContractWalletAddress.ts +npm install -g ts-node typescript @types/node dotenv && ts-node logSpenderSmartWalletAddress.ts ``` @@ -112,7 +116,7 @@ You may need to temporarily set the value of `"module"` in your `tsconfig.json` ### Set up remaining environment variables -Next, make sure you have a Coinbase Developer Platform API key, which you can get [here](https://portal.cdp.coinbase.com/). +Next, make sure you have a Coinbase Developer Platform Client API key, which you can get [here](https://portal.cdp.coinbase.com/). Assign this key to `NEXT_PUBLIC_CDP_API_KEY` in your `.env`. You'll need one more environment variable, which is `BASE_SEPOLIA_PAYMASTER_URL`. @@ -173,6 +177,9 @@ export async function getSpenderBundlerClient() { In `providers.tsx`, update the value of `keysUrl` to be `"https://keys-beta.coinbase.com/connect"`. This will point your app to connect to our public dev environment for smart wallet connections. +We also want to point our chain id to Base Sepolia testnet by setting replacing all instances of `base` +with `baseSepolia` in this file (including the import). + Your config should now look like this: ``` @@ -234,7 +241,7 @@ import { } from "wagmi"; import { Address, Hex, parseUnits } from "viem"; import { useQuery } from "@tanstack/react-query"; -import { spendPermissionManagerAddress } from "@/lib/abi/SpendPermissionManager"; +import { spendPermissionManagerAddress } from "@/app/lib/abi/SpendPermissionManager"; export default function Subscribe() { const [isDisabled, setIsDisabled] = useState(false); @@ -430,9 +437,9 @@ export default function Subscribe() { } ``` -Also be sure to add this new `Subscribe` button component to the top level component in `Page.tsx`. You can put it somewhere between the `
` tags: +Also be sure to add this new `Subscribe` button component to the top level component in `page.tsx`. You can put it somewhere between the `
` tags: -```tsx [Page.tsx] +```tsx [page.tsx] ...
... From b2e3fe8958f559db35b0aaea11aba7c4dbcb3c64 Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Thu, 7 Nov 2024 14:36:11 -0800 Subject: [PATCH 04/10] fix the onchain version --- docs/pages/guides/spend-permissions/quick-start.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/guides/spend-permissions/quick-start.mdx b/docs/pages/guides/spend-permissions/quick-start.mdx index fc952aa..99e57e3 100644 --- a/docs/pages/guides/spend-permissions/quick-start.mdx +++ b/docs/pages/guides/spend-permissions/quick-start.mdx @@ -14,7 +14,7 @@ Set up a boilerplate React/Next app by running the following command and followi Developer Platform API Key, we'll get one of those later. When prompted to use Coinbase Smart Wallet select "yes". ```bash -bun create onchain +bun create onchain@0.0.12 ``` You may need to install the package manager [`bun`](https://bun.sh/docs/installation) From ce9ab11ffcecfa4d4180f7f9581776f7b3b17b79 Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Thu, 7 Nov 2024 15:47:19 -0800 Subject: [PATCH 05/10] simplify tutorial by using EOA for spender --- .../guides/spend-permissions/quick-start.mdx | 191 +++++------------- 1 file changed, 53 insertions(+), 138 deletions(-) diff --git a/docs/pages/guides/spend-permissions/quick-start.mdx b/docs/pages/guides/spend-permissions/quick-start.mdx index 99e57e3..58882ea 100644 --- a/docs/pages/guides/spend-permissions/quick-start.mdx +++ b/docs/pages/guides/spend-permissions/quick-start.mdx @@ -25,98 +25,29 @@ to connect their smart wallet to the application. From here, we'll modify the app to assemble, sign, approve and use a spend permission to spend our users' funds! -### Set up your spender wallet +### Set up your spender wallet and environment -In this example, our app spender wallet will be a brand new Coinbase Smart Wallet. This means the wallet itself is a smart -contract, and this smart contract has at least one owner that can produce signatures on behalf of the wallet. -In our case, this owner will be an EOA. Our app will need access to this owner EOA private key, to generate -signatures, as well as the address of the actual smart contract wallet, which is the spender address actually -being authorized by the spend permission. - -The address of a smart contract wallet is deterministic, and depends on the bytecode of the implementation -contract combined with a salt, which in our case will be an array consisting of the initial owner(s) of the smart contract wallet. -See the [smart wallet repository](https://github.com/coinbase/smart-wallet) for more details about how this works. - -Add these environment variables to your `.env` file: +Add the following variables to your `.env`: ``` -SPENDER_OWNER_PRIVATE_KEY= -NEXT_PUBLIC_SPENDER_WALLET_ADDRESS= +SPENDER_PRIVATE_KEY= +NEXT_PUBLIC_SPENDER_ADDRESS= NEXT_PUBLIC_CDP_API_KEY= BASE_SEPOLIA_PAYMASTER_URL= ``` -The `SPENDER_OWNER_PRIVATE_KEY` will be our spender private key and `NEXT_PUBLIC_SPENDER_WALLET_ADDRESS` will be the address of our spender smart contract wallet. Always secure your private keys appropriately! We insecurely use an environment variable in this demo for simplicity. -You can use a development private key you already have, or generate a random new one. If you have [Foundry](https://book.getfoundry.sh/) installed, you can generate a new -wallet via `cast wallet new`. This wallet won't need to hold any ETH for gas because our spender smart contract wallet will be sponsored by a paymaster. - -Paste your private key as the value for `SPENDER_OWNER_PRIVATE_KEY`. This should be hex-prefixed, i.e. `SPENDER_OWNER_PRIVATE_KEY=0xAbC123...dEf456` - -We'll have to generate the deterministic address of the Smart Wallet owned by this private key so we can store it as a public environment variable. - -Paste the following code into a file at the project root called `logSpenderSmartWalletAddress.ts` - -```ts [logSpenderSmartWalletAddress.tsx] -import { createPublicClient, Hex, http } from "viem"; -import { baseSepolia } from "viem/chains"; -import { privateKeyToAccount } from "viem/accounts"; -import { - toCoinbaseSmartAccount, -} from "viem/account-abstraction"; -import dotenv from "dotenv"; - -dotenv.config(); - -export async function logSpenderSmartWalletAddress() { - const client = createPublicClient({ - chain: baseSepolia, - transport: http(), - }); - - const spenderAccountOwner = privateKeyToAccount( - process.env.SPENDER_OWNER_PRIVATE_KEY! as Hex - ); - console.log("spenderAccountOwner", spenderAccountOwner.address); - - const spenderAccount = await toCoinbaseSmartAccount({ - client, - owners: [spenderAccountOwner], - }); -console.log("Spender Smart Wallet Address:", spenderAccount.address); -} - -async function main() { - await logSpenderSmartWalletAddress(); -} - -if (require.main === module) { - main().catch((error) => { - console.error(error); - process.exit(1); - }); -} -``` - -Run this script to log the counterfactual address of your new smart contract wallet and assign this address to the -`NEXT_PUBLIC_SPENDER_WALLET_ADDRESS` variable in your .env: - -```bash -npm install -g ts-node typescript @types/node dotenv && ts-node logSpenderSmartWalletAddress.ts -``` - - -You may need to temporarily set the value of `"module"` in your `tsconfig.json` to `"commonjs"` to run this script. - - +Our spender will need to sign transactions from our app, so we'll create a local wallet for our spender. We'll need a private key and the corresponding ethereum +address in our environment variables. You can use a development private key you already have, or generate a random new one. +If you have [Foundry](https://book.getfoundry.sh/) installed, you can generate a new wallet via `cast wallet new`. Assign the private key and address to +`SPENDER_PRIVATE_KEY` and `NEXT_PUBLIC_SPENDER_ADDRESS`, respectively. -### Set up remaining environment variables -Next, make sure you have a Coinbase Developer Platform Client API key, which you can get [here](https://portal.cdp.coinbase.com/). +Next, make sure you have a Coinbase Developer Platform Client API key (different from Secret API Key), which you can get [here](https://portal.cdp.coinbase.com/). Assign this key to `NEXT_PUBLIC_CDP_API_KEY` in your `.env`. You'll need one more environment variable, which is `BASE_SEPOLIA_PAYMASTER_URL`. @@ -126,49 +57,34 @@ This one's easy if you already have your CDP API key: ### Create a spender client -Our client is what our app will use to communicate with the blockchain. In this example, our client is a Coinbase Smart Wallet, -and we'll use a [paymaster](/guides/paymasters.mdx) to sponsor our transactions so we don't have to worry -about having ETH in the spender account. +Our client is what our app will use to communicate with the blockchain. Create a sibling directory to `app` called `lib` and add the following `spender.ts` file to create your spender client. ```ts [lib/spender. ts] -import { createPublicClient, Hex, http } from "viem"; +import { createPublicClient, createWalletClient, Hex, http } from "viem"; import { baseSepolia } from "viem/chains"; import { privateKeyToAccount } from "viem/accounts"; -import { - createBundlerClient, - createPaymasterClient, - toCoinbaseSmartAccount, -} from "viem/account-abstraction"; -export async function getSpenderBundlerClient() { +export async function getPublicClient() { const client = createPublicClient({ chain: baseSepolia, transport: http(), }); + return client; +} - const spenderAccountOwner = privateKeyToAccount( - process.env.SPENDER_OWNER_PRIVATE_KEY! as Hex +export async function getSpenderWalletClient() { + const spenderAccount = privateKeyToAccount( + process.env.SPENDER_PRIVATE_KEY! as Hex ); - const spenderAccount = await toCoinbaseSmartAccount({ - client, - owners: [spenderAccountOwner], - }); - - const paymasterClient = createPaymasterClient({ - transport: http(process.env.BASE_SEPOLIA_PAYMASTER_URL), - }); - - const spenderBundlerClient = createBundlerClient({ + const spenderWallet = await createWalletClient({ account: spenderAccount, - client, - paymaster: paymasterClient, - transport: http(process.env.BASE_SEPOLIA_PAYMASTER_URL), + chain: baseSepolia, + transport: http(), }); - - return spenderBundlerClient; + return spenderWallet; } ``` @@ -180,7 +96,7 @@ This will point your app to connect to our public dev environment for smart wall We also want to point our chain id to Base Sepolia testnet by setting replacing all instances of `base` with `baseSepolia` in this file (including the import). -Your config should now look like this: +Your config in `providers.tsx` should now look like this: ``` const config = createConfig({ @@ -278,7 +194,7 @@ export default function Subscribe() { const spendPermission = { account: accountAddress, // User wallet address - spender: process.env.NEXT_PUBLIC_SPENDER_WALLET_ADDRESS! as Address, // Spender smart contract wallet address + spender: process.env.NEXT_PUBLIC_SPENDER_ADDRESS! as Address, // Spender smart contract wallet address token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" as Address, // ETH (https://eips.ethereum.org/EIPS/eip-7528) allowance: parseUnits("10", 18), period: 86400, // seconds in a day @@ -476,7 +392,7 @@ export default function Subscribe() { // Define a `SpendPermission` to request from the user // [!code focus] const spendPermission = { // [!code focus] account: accountAddress, // User wallet address // [!code focus] - spender: process.env.NEXT_PUBLIC_SPENDER_WALLET_ADDRESS! as Address, // Spender smart contract wallet address // [!code focus] + spender: process.env.NEXT_PUBLIC_SPENDER_ADDRESS! as Address, // Spender smart contract wallet address // [!code focus] token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" as Address, // ETH (https://eips.ethereum.org/EIPS/eip-7528) // [!code focus] allowance: parseUnits("10", 18), // [!code focus] period: 86400, // seconds // [!code focus] @@ -609,44 +525,45 @@ code: ```ts [route.tsx] import { NextRequest, NextResponse } from "next/server"; -import { getSpenderBundlerClient } from "../../lib/spender"; +import { getPublicClient, getSpenderWalletClient } from "../../lib/spender"; import { spendPermissionManagerAbi, spendPermissionManagerAddress, } from "../../lib/abi/SpendPermissionManager"; export async function POST(request: NextRequest) { - const spenderBundlerClient = await getSpenderBundlerClient(); + const spenderBundlerClient = await getSpenderWalletClient(); + const publicClient = await getPublicClient(); try { const body = await request.json(); const { spendPermission, signature } = body; - const userOpHash = await spenderBundlerClient.sendUserOperation({ - calls: [ - { - abi: spendPermissionManagerAbi, - functionName: "approveWithSignature", - to: spendPermissionManagerAddress, - args: [spendPermission, signature], - }, - { - abi: spendPermissionManagerAbi, - functionName: "spend", - to: spendPermissionManagerAddress, - args: [spendPermission, "1"], // spend 1 wei - }, - ], + const approvalTxnHash = await spenderBundlerClient.writeContract({ + address: spendPermissionManagerAddress, + abi: spendPermissionManagerAbi, + functionName: "approveWithSignature", + args: [spendPermission, signature], }); - const userOpReceipt = - await spenderBundlerClient.waitForUserOperationReceipt({ - hash: userOpHash, - }); + const approvalReceipt = await publicClient.waitForTransactionReceipt({ + hash: approvalTxnHash, + }); + + const spendTxnHash = await spenderBundlerClient.writeContract({ + address: spendPermissionManagerAddress, + abi: spendPermissionManagerAbi, + functionName: "spend", + args: [spendPermission, "1"], + }); + + const spendReceipt = await publicClient.waitForTransactionReceipt({ + hash: spendTxnHash, + }); return NextResponse.json({ - status: userOpReceipt.success ? "success" : "failure", - transactionHash: userOpReceipt.receipt.transactionHash, - transactionUrl: `https://sepolia.basescan.org/tx/${userOpReceipt.receipt.transactionHash}`, + status: spendReceipt.status ? "success" : "failure", + transactionHash: spendReceipt.transactionHash, + transactionUrl: `https://sepolia.basescan.org/tx/${spendReceipt.transactionHash}`, }); } catch (error) { console.error(error); @@ -659,23 +576,21 @@ This code is using our spender client to do two things: 1. calls `approveWithSignature` to approve the spend permission 2. calls `spend` to make use of our allowance and spend our user's funds -Since our spender is a smart contract wallet, notice that these calls are formulated as userOperations instead of -direct transactions. They'll be submitted to the blockchain by a bundler and the gas will be subsidized by our -Coinbase Developer Platform paymaster. ### Try out your app Run your app locally with `bun run dev` and visit `localhost:3000`. -You should see a "Connect wallet" button in the top right corner; click that. +When you click the "Subscribe" button you should be prompted to create or connect your Smart Wallet. You can create a new Smart Wallet via the popup. Note that you'll need a little ETH in this wallet to fund the deployment of your account. If you don't have any testnet ETH, try this [Coinbase faucet](https://portal.cdp.coinbase.com/products/faucet). Once your wallet is created and funded, return to the app and click "Subscribe". -Sign the prompts, which will deploy your new Smart Wallet (if undeployed), and then prompt you to sign over the spend permission. +Sign the prompts, which will deploy your new Smart Wallet (if undeployed), and then prompt you to sign the spend permission requested by the app. -Once you've subscribed, you should see spend transactions happening automatically every few seconds. Click the transaction links to check them out on Etherscan. +Once you've subscribed, you should see a spend transaction hash show up on screen after a few seconds. +You can prompt new spends by clicking the "Collect Subscription" button. Click the transaction links to check them out on Etherscan! We've made it! 🎉 From 18805291a5ef650e71489ee17020cc9f1650c44c Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Thu, 7 Nov 2024 15:49:48 -0800 Subject: [PATCH 06/10] add note about spender wallet needing eth --- docs/pages/guides/spend-permissions/quick-start.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/pages/guides/spend-permissions/quick-start.mdx b/docs/pages/guides/spend-permissions/quick-start.mdx index 58882ea..35efc3e 100644 --- a/docs/pages/guides/spend-permissions/quick-start.mdx +++ b/docs/pages/guides/spend-permissions/quick-start.mdx @@ -586,11 +586,13 @@ When you click the "Subscribe" button you should be prompted to create or connec You can create a new Smart Wallet via the popup. Note that you'll need a little ETH in this wallet to fund the deployment of your account. If you don't have any testnet ETH, try this [Coinbase faucet](https://portal.cdp.coinbase.com/products/faucet). -Once your wallet is created and funded, return to the app and click "Subscribe". +Before you continue, note that you'll also need a little testnet ETH in your spender wallet, which will need to fund the gas for the transactions. + +Once your wallet is created and both wallets are funded, return to the app and click "Subscribe". Sign the prompts, which will deploy your new Smart Wallet (if undeployed), and then prompt you to sign the spend permission requested by the app. Once you've subscribed, you should see a spend transaction hash show up on screen after a few seconds. -You can prompt new spends by clicking the "Collect Subscription" button. Click the transaction links to check them out on Etherscan! +You can prompt new spends by clicking the "Collect Subscription" button. Click the transactions to check them out on Etherscan! We've made it! 🎉 From 9895d7492599b90f145c5ba8bf7821aeea65079b Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Fri, 8 Nov 2024 05:54:41 -0800 Subject: [PATCH 07/10] update copy about local wallet --- .../guides/spend-permissions/quick-start.mdx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pages/guides/spend-permissions/quick-start.mdx b/docs/pages/guides/spend-permissions/quick-start.mdx index 35efc3e..c88b2aa 100644 --- a/docs/pages/guides/spend-permissions/quick-start.mdx +++ b/docs/pages/guides/spend-permissions/quick-start.mdx @@ -42,8 +42,7 @@ Always secure your private keys appropriately! We insecurely use an environment variable in this demo for simplicity. -Our spender will need to sign transactions from our app, so we'll create a local wallet for our spender. We'll need a private key and the corresponding ethereum -address in our environment variables. You can use a development private key you already have, or generate a random new one. +Our spender will need to sign transactions from our app, so we'll create a wallet (private key and address) for our spender. If you have [Foundry](https://book.getfoundry.sh/) installed, you can generate a new wallet via `cast wallet new`. Assign the private key and address to `SPENDER_PRIVATE_KEY` and `NEXT_PUBLIC_SPENDER_ADDRESS`, respectively. @@ -90,13 +89,13 @@ export async function getSpenderWalletClient() { ### Configure the Smart Wallet URL -In `providers.tsx`, update the value of `keysUrl` to be `"https://keys-beta.coinbase.com/connect"`. +In `app/providers.tsx`, update the value of `keysUrl` to be `"https://keys-beta.coinbase.com/connect"`. This will point your app to connect to our public dev environment for smart wallet connections. We also want to point our chain id to Base Sepolia testnet by setting replacing all instances of `base` with `baseSepolia` in this file (including the import). -Your config in `providers.tsx` should now look like this: +Your config in `app/providers.tsx` should now look like this: ``` const config = createConfig({ @@ -586,13 +585,14 @@ When you click the "Subscribe" button you should be prompted to create or connec You can create a new Smart Wallet via the popup. Note that you'll need a little ETH in this wallet to fund the deployment of your account. If you don't have any testnet ETH, try this [Coinbase faucet](https://portal.cdp.coinbase.com/products/faucet). -Before you continue, note that you'll also need a little testnet ETH in your spender wallet, which will need to fund the gas for the transactions. +Note that we'll need a little bit of base sepolia ETH in both wallet addresses (the "user" wallet and the "app" wallet). +In a more involved implementation you would use a paymaster to eliminate this requirement. +For now, If you don't have any base sepolia ETH, try this [Coinbase faucet](https://portal.cdp.coinbase.com/products/faucet). -Once your wallet is created and both wallets are funded, return to the app and click "Subscribe". -Sign the prompts, which will deploy your new Smart Wallet (if undeployed), and then prompt you to sign the spend permission requested by the app. +Once your wallet is created and both wallets are funded, return to the app and click "Subscribe", then sign the prompt to allow the spend permission. Once you've subscribed, you should see a spend transaction hash show up on screen after a few seconds. -You can prompt new spends by clicking the "Collect Subscription" button. Click the transactions to check them out on Etherscan! +You can prompt subsequent spends by clicking the "Collect Subscription" button. Click the transactions to check them out on Etherscan! We've made it! 🎉 From 6978c1c27ca1889f8c31aa3d79527b104667a37c Mon Sep 17 00:00:00 2001 From: Conner Swenberg Date: Sat, 9 Nov 2024 02:56:31 +0700 Subject: [PATCH 08/10] Update docs/pages/guides/spend-permissions/quick-start.mdx --- docs/pages/guides/spend-permissions/quick-start.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/guides/spend-permissions/quick-start.mdx b/docs/pages/guides/spend-permissions/quick-start.mdx index c88b2aa..c5b690c 100644 --- a/docs/pages/guides/spend-permissions/quick-start.mdx +++ b/docs/pages/guides/spend-permissions/quick-start.mdx @@ -14,7 +14,7 @@ Set up a boilerplate React/Next app by running the following command and followi Developer Platform API Key, we'll get one of those later. When prompted to use Coinbase Smart Wallet select "yes". ```bash -bun create onchain@0.0.12 +bun create onchain@latest ``` You may need to install the package manager [`bun`](https://bun.sh/docs/installation) From fdf2434001950a94cde514f3ffae77738aaf4f25 Mon Sep 17 00:00:00 2001 From: Conner Swenberg Date: Sat, 9 Nov 2024 02:57:05 +0700 Subject: [PATCH 09/10] Update docs/pages/guides/spend-permissions/quick-start.mdx --- docs/pages/guides/spend-permissions/quick-start.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/guides/spend-permissions/quick-start.mdx b/docs/pages/guides/spend-permissions/quick-start.mdx index c5b690c..b7ad7d4 100644 --- a/docs/pages/guides/spend-permissions/quick-start.mdx +++ b/docs/pages/guides/spend-permissions/quick-start.mdx @@ -89,7 +89,7 @@ export async function getSpenderWalletClient() { ### Configure the Smart Wallet URL -In `app/providers.tsx`, update the value of `keysUrl` to be `"https://keys-beta.coinbase.com/connect"`. +In `app/providers.tsx`, update the value of `keysUrl` to be `"https://keys-dev.coinbase.com/connect"`. This will point your app to connect to our public dev environment for smart wallet connections. We also want to point our chain id to Base Sepolia testnet by setting replacing all instances of `base` From 63354ff68a1fa5de25dee51f4a6cf473e3fc8386 Mon Sep 17 00:00:00 2001 From: Conner Swenberg Date: Sat, 9 Nov 2024 02:57:17 +0700 Subject: [PATCH 10/10] Update docs/pages/guides/spend-permissions/quick-start.mdx --- docs/pages/guides/spend-permissions/quick-start.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/guides/spend-permissions/quick-start.mdx b/docs/pages/guides/spend-permissions/quick-start.mdx index b7ad7d4..ea49764 100644 --- a/docs/pages/guides/spend-permissions/quick-start.mdx +++ b/docs/pages/guides/spend-permissions/quick-start.mdx @@ -107,7 +107,7 @@ const config = createConfig({ | "smartWalletOnly" | "all", // @ts-ignore - keysUrl: "https://keys-beta.coinbase.com/connect" + keysUrl: "https://keys-dev.coinbase.com/connect" }), ], storage: createStorage({