From 45a569ca190f540d578e58d824b81c60dbd5fb5c Mon Sep 17 00:00:00 2001 From: jakehobbs Date: Thu, 22 Jan 2026 16:22:33 -0800 Subject: [PATCH 1/7] docs: first pass of 7702 default --- .../cross-chain-swap-tokens/api.mdx | 50 ++----- .../cross-chain-swap-tokens/client.mdx | 27 ++-- .../pay-gas-with-any-token/api-postop.mdx | 24 +--- .../pay-gas-with-any-token/api-preop.mdx | 32 +---- .../pay-gas-with-any-token/client.mdx | 19 +-- .../transactions/retry-transactions/api.mdx | 40 +----- .../retry-transactions/client.mdx | 21 +-- .../send-batch-transactions/api.mdx | 30 +---- .../send-batch-transactions/client.mdx | 19 +-- .../send-parallel-transactions/api.mdx | 83 ++---------- .../send-parallel-transactions/client.mdx | 21 +-- .../transactions/send-transactions/api.mdx | 125 ++++++++++-------- .../transactions/send-transactions/client.mdx | 27 ++-- .../encoding-function-data/api.mdx | 4 +- .../encoding-function-data/client.mdx | 27 ++-- .../prepare-calls/client.mdx | 27 ++-- .../signing/sign-messages/client-raw.mdx | 19 +-- .../signing/sign-messages/client-text.mdx | 19 +-- .../signing/sign-typed-data/client.mdx | 19 +-- .../pages/transactions/sponsor-gas/api.mdx | 26 +--- .../pages/transactions/sponsor-gas/client.mdx | 19 +-- .../pages/transactions/swap-tokens/api.mdx | 52 ++------ .../pages/transactions/swap-tokens/client.mdx | 25 ++-- .../pages/transactions/using-eip-7702/api.mdx | 12 +- .../transactions/using-eip-7702/client.mdx | 9 +- .../transactions/using-eip-7702/index.mdx | 89 ++++++++++++- 26 files changed, 309 insertions(+), 556 deletions(-) diff --git a/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx b/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx index 508b3e7b6..518ba6782 100644 --- a/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx +++ b/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx @@ -2,40 +2,6 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. - - -```bash -curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ - -H "Content-Type: application/json" \ - -d '{ - "jsonrpc": "2.0", - "id": 1, - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "{SIGNER_ADDRESS}" - } - ] - }' -``` - -This returns: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "accountAddress": "ACCOUNT_ADDRESS", - "id": "ACCOUNT_ID" - } -} -``` - -For other potential responses, [check out the API reference!](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account) - - - @@ -43,7 +9,7 @@ For other potential responses, [check out the API reference!](/docs/wallets/api- additional actions after a cross-chain swap. -Request a cross-chain swap quote by specifying both the source chain (`chainId`) and destination chain (`toChainId`). In addition, just like in [single-chain swaps](/wallets/transactions/swap-tokens) you can specify either a `minimumToAmount` or a `fromAmount`. +Use your signer address directly as the `from` field. Request a cross-chain swap quote by specifying both the source chain (`chainId`) and destination chain (`toChainId`). In addition, just like in [single-chain swaps](/wallets/transactions/swap-tokens) you can specify either a `minimumToAmount` or a `fromAmount`. If you're using an EOA or just want the raw array of calls returned, pass the @@ -59,7 +25,7 @@ curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ "method": "wallet_requestQuote_v0", "params": [ { - "from": "{ACCOUNT_ADDRESS_FROM_STEP_1}", + "from": "{SIGNER_ADDRESS}", "chainId": "{SOURCE_CHAIN_ID}", "toChainId": "{DESTINATION_CHAIN_ID}", "fromToken": "{FROM_TOKEN}", @@ -110,6 +76,8 @@ This returns: Note the `callId` in the response! You'll use this to track the cross-chain swap status. Also note the `signatureRequest` - this is what you need to sign, and the returned `data` field is what you'll need to send the transaction. +On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for handling first-time authorization signing. + @@ -124,7 +92,7 @@ Alternatively, you can sign the raw payload with a simple `eth_sign` but this RP -Pass the `callId` from Step 2 to `sendPreparedCalls`. This makes the response return the same `callId` with additional cross-chain status tracking information: +Pass the `callId` from Step 1 to `sendPreparedCalls`. This makes the response return the same `callId` with additional cross-chain status tracking information: ```bash curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ @@ -134,13 +102,13 @@ curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ "method": "wallet_sendPreparedCalls", "params": [ { - "callId": "{CALL_ID_FROM_STEP_2}", + "callId": "{CALL_ID_FROM_STEP_1}", "type": "user-operation-v070", - "data": "{DATA_FROM_STEP_2}", + "data": "{DATA_FROM_STEP_1}", "chainId": "{SOURCE_CHAIN_ID}", "signature": { "type": "secp256k1", - "data": "{SIGNATURE_FROM_STEP_3}" + "data": "{SIGNATURE_FROM_STEP_2}" } } ], @@ -178,7 +146,7 @@ curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ "method": "wallet_getCallsStatus", "params": [ [ - "{CALL_ID_FROM_STEP_2_OR_STEP_4}" + "{CALL_ID_FROM_STEP_1_OR_STEP_3}" ] ], "id": 1 diff --git a/fern/wallets/pages/transactions/cross-chain-swap-tokens/client.mdx b/fern/wallets/pages/transactions/cross-chain-swap-tokens/client.mdx index 9b64d2af2..a88ff1d83 100644 --- a/fern/wallets/pages/transactions/cross-chain-swap-tokens/client.mdx +++ b/fern/wallets/pages/transactions/cross-chain-swap-tokens/client.mdx @@ -14,9 +14,7 @@ You'll need the following env variables: ```ts title="requestCrossChainQuote.ts" import { swapActions } from "@account-kit/wallet-client/experimental"; - import { client } from "./client"; - - const account = await client.requestAccount(); + import { client, signer } from "./client"; // Add the swap actions to the client const swapClient = client.extend(swapActions); @@ -24,7 +22,7 @@ You'll need the following env variables: // Request the cross-chain swap quote // Note: toChainId specifies the destination chain for the swap const { quote, callId, ...calls } = await swapClient.requestQuoteV0({ - from: account.address, + from: await signer.getAddress(), // Your wallet address toChainId: "0x...", // Destination chain ID fromToken: "0x...", toToken: "0x...", @@ -83,24 +81,17 @@ You'll need the following env variables: import { alchemy, sepolia } from "@account-kit/infra"; import { createSmartWalletClient } from "@account-kit/wallet-client"; - const clientParams = { + export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, + ) + + export const client = createSmartWalletClient({ transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - process.env.PRIVATE_KEY! as Hex, - ), + signer, policyId: process.env.ALCHEMY_POLICY_ID!, // Optional: If you're using a gas manager policy - }; - - const clientWithoutAccount = createSmartWalletClient(clientParams); - - const account = await clientWithoutAccount.requestAccount(); - - export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, }); - ``` +``` diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx index 01bb440e6..fca20dd1b 100644 --- a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx +++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx @@ -3,28 +3,8 @@ reference](/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/ for full descriptions of the parameters used in the following example. - - If the user does not yet have a smart account, you must create one. -```bash - ACCOUNT_ADDRESS=$(curl --request POST \ - --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ - --header 'accept: application/json' \ - --data ' -{ - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "'$SIGNER_ADDRESS'" - } - ] -} -' | jq -r '.result.accountAddress') -``` - - Prepare calls using the `paymasterService` capability. + Prepare calls using the `paymasterService` capability. Use your signer address directly as the `from` field. ```bash curl --request POST \ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ @@ -55,7 +35,7 @@ curl --request POST \ "to": "0x0000000000000000000000000000000000000000" } ], - "from": "'$ACCOUNT_ADDRESS'", + "from": "'$SIGNER_ADDRESS'", "chainId": "'$CHAIN_ID'" } ] diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx index ee4f7ba69..003e68d97 100644 --- a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx +++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx @@ -1,28 +1,6 @@ - - If the user does not yet have a smart account, you must create one. - - ```bash - ACCOUNT_ADDRESS=$(curl --request POST \ - --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ - --header 'accept: application/json' \ - --data ' - { - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "'$SIGNER_ADDRESS'" - } - ] - } - ' | jq -r '.result.accountAddress') - ``` - - - Prepare calls using the `paymasterService` capability. + Prepare calls using the `paymasterService` capability. Use your signer address directly as the `from` field. ```bash curl --request POST \ @@ -54,7 +32,7 @@ "to": "0x0000000000000000000000000000000000000000" } ], - "from": "'$ACCOUNT_ADDRESS'", + "from": "'$SIGNER_ADDRESS'", "chainId": "'$CHAIN_ID'" } ] @@ -63,7 +41,7 @@ - If the response to step 2 is a type `paymaster-permit`, then the user must sign the signature request and return via a second call to `wallet_prepareCalls`. Else, skip to step 5. + If the response to step 1 is a type `paymaster-permit`, then the user must sign the signature request and return via a second call to `wallet_prepareCalls`. Else, skip to step 4. The `data` field on the `paymaster-permit` response contains a [Permit typed message](https://eips.ethereum.org/EIPS/eip-2612). It is recommended that the user checks the fields of this message prior to calculating its hash. The `signatureRequest` field on the `paymaster-permit` response is a required signature over the calculated hash. This is typically an ERC-712 typed signature envelope with a field for the hash to sign over. @@ -72,7 +50,7 @@ After signing, another call to `wallet_prepareCalls` is needed to encode the permit into the operation. As a convenience, the `paymaster-permit` response type returns a `modifiedRequest` parameter that modifies the initial request with the permit details. The second request is the `modifiedRequest` with an additional `paymasterPermitSignature` field set to the - signature from step 3. The user can also choose to recreate the request and set the corresponding fields in the `paymasterService` capability to the details returned in the permit signature. + signature from step 2. The user can also choose to recreate the request and set the corresponding fields in the `paymasterService` capability to the details returned in the permit signature. For example: ```json @@ -89,7 +67,7 @@ } } } - ... // same request as step 2 + ... // same request as step 1 "paymasterPermitSignature": "0x..." } ``` diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/client.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/client.mdx index 42101a9de..6ce002f22 100644 --- a/fern/wallets/pages/transactions/pay-gas-with-any-token/client.mdx +++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/client.mdx @@ -50,23 +50,16 @@ export const config = { approveAmount: toHex(10000000n), // 10 USDC }; -const clientParams = { +export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, +); + +export const client = createSmartWalletClient({ transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - process.env.PRIVATE_KEY! as Hex, - ), -}; - -const clientWithoutAccount = createSmartWalletClient(clientParams); - -const account = await clientWithoutAccount.requestAccount(); - -export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, + signer, }); ``` diff --git a/fern/wallets/pages/transactions/retry-transactions/api.mdx b/fern/wallets/pages/transactions/retry-transactions/api.mdx index 6c6d6dd35..43fed267a 100644 --- a/fern/wallets/pages/transactions/retry-transactions/api.mdx +++ b/fern/wallets/pages/transactions/retry-transactions/api.mdx @@ -2,42 +2,10 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. - - -```bash -curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ - -H "Content-Type: application/json" \ - -d '{ - "jsonrpc": "2.0", - "id": 1, - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "{SIGNER_ADDRESS}" - } - ] - }' -``` - -This returns: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "accountAddress": "ACCOUNT_ADDRESS", - "id": "ACCOUNT_ID" - } -} -``` - -For other potential responses, [check out the API reference!](https://www.alchemy.com/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account) - - - +Use your signer address directly as the `from` field. + ```bash curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ -H "Content-Type: application/json" \ @@ -54,7 +22,7 @@ curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ "data": "{DATA}" } ], - "from": "{ACCOUNT_ADDRESS}", + "from": "{SIGNER_ADDRESS}", "chainId": "{CHAIN_ID}", "capabilities": { "paymasterService": { @@ -150,7 +118,7 @@ curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ "data": "{SAME_DATA}" } ], - "from": "{ACCOUNT_ADDRESS}", + "from": "{SIGNER_ADDRESS}", "chainId": "{CHAIN_ID}", "capabilities": { "paymasterService": { diff --git a/fern/wallets/pages/transactions/retry-transactions/client.mdx b/fern/wallets/pages/transactions/retry-transactions/client.mdx index c8a1d80b6..8fd263d58 100644 --- a/fern/wallets/pages/transactions/retry-transactions/client.mdx +++ b/fern/wallets/pages/transactions/retry-transactions/client.mdx @@ -72,7 +72,7 @@ You'll need the following env variables: ```ts title="client.ts" import "dotenv/config"; - import { type Address, type Hex, toHex } from "viem"; + import type { Hex } from "viem"; import { LocalAccountSigner } from "@aa-sdk/core"; import { alchemy, sepolia } from "@account-kit/infra"; import { createSmartWalletClient } from "@account-kit/wallet-client"; @@ -81,23 +81,16 @@ You'll need the following env variables: policyId: process.env.ALCHEMY_POLICY_ID!, }; - const clientParams = { + export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, + ); + + export const client = createSmartWalletClient({ transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - process.env.PRIVATE_KEY! as Hex, - ), - }; - - const clientWithoutAccount = createSmartWalletClient(clientParams); - - const account = await clientWithoutAccount.requestAccount(); - - export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, + signer, }); ``` diff --git a/fern/wallets/pages/transactions/send-batch-transactions/api.mdx b/fern/wallets/pages/transactions/send-batch-transactions/api.mdx index ce92c16f0..cc6be62a2 100644 --- a/fern/wallets/pages/transactions/send-batch-transactions/api.mdx +++ b/fern/wallets/pages/transactions/send-batch-transactions/api.mdx @@ -8,28 +8,8 @@ reference](/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/ for full descriptions of the parameters used in the following example. - - If the user does not yet have a smart account, you must create one. -```bash twoslash - ACCOUNT_ADDRESS=$(curl --request POST \ - --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ - --header 'accept: application/json' \ - --data ' -{ - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "'$SIGNER_ADDRESS'" - } - ] -} -' | jq -r '.result.accountAddress') -``` - - Prepare calls with multiple calls specified in the batch. + Prepare calls with multiple calls specified in the batch. Use your signer address directly as the `from` field. ```bash twoslash curl --request POST \ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ @@ -44,14 +24,14 @@ curl --request POST \ "calls": [ { "to": "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443", - "data": "'$(cast calldata "approve(address,uint256)" 0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C $(cast parse-units 5000 6))'" + "data": "'$(cast calldata "approve(address,uint256)" 0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C $(cast parse-units 5000 6))'" }, { "to": "0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C", "data": "'$(cast calldata "swapUSDCtoWETH(uint256,uint256)" $(cast parse-units 5000 6) $(cast parse-units 1 18))'" - } + } ], - "from": "'$ACCOUNT_ADDRESS'", + "from": "'$SIGNER_ADDRESS'", "chainId": "'$CHAIN_ID_HEX'" } ] @@ -59,6 +39,6 @@ curl --request POST \ ``` - Sign the returned signature request and send using `wallet_sendPreparedCalls`. + Sign the returned signature request and send using `wallet_sendPreparedCalls`. See [Send Transactions](/wallets/transactions/send-transactions) for a complete example including handling first-time 7702 authorization. diff --git a/fern/wallets/pages/transactions/send-batch-transactions/client.mdx b/fern/wallets/pages/transactions/send-batch-transactions/client.mdx index af49a12c3..56b5a2592 100644 --- a/fern/wallets/pages/transactions/send-batch-transactions/client.mdx +++ b/fern/wallets/pages/transactions/send-batch-transactions/client.mdx @@ -49,23 +49,16 @@ import { LocalAccountSigner } from "@aa-sdk/core"; import { alchemy, sepolia } from "@account-kit/infra"; import { createSmartWalletClient } from "@account-kit/wallet-client"; -const clientParams = { +export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, +); + +export const client = createSmartWalletClient({ transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - process.env.PRIVATE_KEY! as Hex, - ), -}; - -const clientWithoutAccount = createSmartWalletClient(clientParams); - -const account = await clientWithoutAccount.requestAccount(); - -export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, + signer, }); ``` diff --git a/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx b/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx index 749613caa..651b0bc2b 100644 --- a/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx +++ b/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx @@ -1,40 +1,8 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. - - ```bash - curl -X POST https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ - -H "Content-Type: application/json" \ - -d '{ - "jsonrpc": "2.0", - "id": 1, - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "{SIGNER_ADDRESS}" - } - ] - }' - ``` - - This returns: - - ```json - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "accountAddress": "ACCOUNT_ADDRESS", - "id": "ACCOUNT_ID" - } - } - ``` - - For other potential responses, [check out the API reference!](https://www.alchemy.com/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account) - - - To send additional parallel calls, we have to use the `nonceOverride` capability. This allows us to specify a `nonceKey` parameter, a hex value that fits inside a `uint152` and differentiates transactions. + Use your signer address directly as the `from` field. To send parallel calls, use the `nonceOverride` capability. This allows us to specify a `nonceKey` parameter, a hex value that fits inside a `uint152` and differentiates transactions. In production, as long as the nonce key override is nonzero (zero is the default), and different between `prepareCalls` requests, the transactions will be processed in parallel. @@ -57,7 +25,7 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. "data": "0x" } ], - "from": "{ACCOUNT_ADDRESS}", + "from": "{SIGNER_ADDRESS}", "chainId": "{CHAIN_ID}", "capabilities": { "paymasterService": { @@ -89,7 +57,7 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. "data": "0x" } ], - "from": "{ACCOUNT_ADDRESS}", + "from": "{SIGNER_ADDRESS}", "chainId": "{CHAIN_ID}", "capabilities": { "paymasterService": { @@ -145,20 +113,20 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. "data": [ { "type": "user-operation-v070", - "data": {DATA_ONE_FROM_STEP_2}, + "data": {DATA_ONE_FROM_STEP_1}, "chainId": "{CHAIN_ID}", "signature": { "type": "secp256k1", - "data": "{SIGNATURE_ONE_FROM_STEP_3}" + "data": "{SIGNATURE_ONE_FROM_STEP_2}" } }, { "type": "user-operation-v070", - "data": {DATA_TWO_FROM_STEP_2}, + "data": {DATA_TWO_FROM_STEP_1}, "chainId": "{CHAIN_ID}", "signature": { "type": "secp256k1", - "data": "{SIGNATURE_TWO_FROM_STEP_3}" + "data": "{SIGNATURE_TWO_FROM_STEP_2}" } } ] @@ -196,7 +164,7 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. "method": "wallet_getCallsStatus", "params": [ [ - "{PREPARED_CALL_ID_ONE_OR_TWO_FROM_STEP_4}", + "{PREPARED_CALL_ID_ONE_OR_TWO_FROM_STEP_3}", ] ], "id": 1 @@ -261,36 +229,7 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. API_URL="https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY" echo "Starting parallel user operations flow..." - - # Step 0: Request account address - echo "Requesting account address for signer: $SIGNER_ADDRESS" - ACCOUNT_RESPONSE=$(curl -s --request POST \ - --url "$API_URL" \ - --header 'accept: application/json' \ - --header 'content-type: application/json' \ - --data '{ - "jsonrpc": "2.0", - "id": 1, - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "'"$SIGNER_ADDRESS"'" - } - ] - }') - - # Check for errors in account request - if [[ $(echo "$ACCOUNT_RESPONSE" | jq -r '.error') != "null" ]]; then - echo "❌ Error in wallet_requestAccount: $(echo "$ACCOUNT_RESPONSE" | jq -r '.error.message')" - exit 1 - fi - - # Extract account address - SENDER=$(echo "$ACCOUNT_RESPONSE" | jq -r '.result.accountAddress') - ACCOUNT_ID=$(echo "$ACCOUNT_RESPONSE" | jq -r '.result.id') - - echo "Account Address: $SENDER" - echo "Account ID: $ACCOUNT_ID" + echo "Using signer address: $SIGNER_ADDRESS" # Step 1: Prepare first call (nonce key 0x01) echo "Preparing first call (nonce key 0x01)..." @@ -310,7 +249,7 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. "data": "0x" } ], - "from": "'"$SENDER"'", + "from": "'"$SIGNER_ADDRESS"'", "chainId": "'"$CHAIN_ID"'", "capabilities": { "paymasterService": { @@ -342,7 +281,7 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. "data": "0x" } ], - "from": "'"$SENDER"'", + "from": "'"$SIGNER_ADDRESS"'", "chainId": "'"$CHAIN_ID"'", "capabilities": { "paymasterService": { diff --git a/fern/wallets/pages/transactions/send-parallel-transactions/client.mdx b/fern/wallets/pages/transactions/send-parallel-transactions/client.mdx index fac4de96a..023ec271d 100644 --- a/fern/wallets/pages/transactions/send-parallel-transactions/client.mdx +++ b/fern/wallets/pages/transactions/send-parallel-transactions/client.mdx @@ -68,7 +68,7 @@ You'll need the following env variables: ```ts title="client.ts" import "dotenv/config"; - import { type Address, type Hex, toHex } from "viem"; + import type { Hex } from "viem"; import { LocalAccountSigner } from "@aa-sdk/core"; import { alchemy, sepolia } from "@account-kit/infra"; import { createSmartWalletClient } from "@account-kit/wallet-client"; @@ -77,23 +77,16 @@ You'll need the following env variables: policyId: process.env.ALCHEMY_POLICY_ID!, }; - const clientParams = { + export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, + ); + + export const client = createSmartWalletClient({ transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - process.env.PRIVATE_KEY! as Hex, - ), - }; - - const clientWithoutAccount = createSmartWalletClient(clientParams); - - const account = await clientWithoutAccount.requestAccount(); - - export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, + signer, }); ``` diff --git a/fern/wallets/pages/transactions/send-transactions/api.mdx b/fern/wallets/pages/transactions/send-transactions/api.mdx index a3f374321..8f029e3bb 100644 --- a/fern/wallets/pages/transactions/send-transactions/api.mdx +++ b/fern/wallets/pages/transactions/send-transactions/api.mdx @@ -21,33 +21,9 @@ SIGNER_PRIVATE_KEY=your-signer-private-key - - -If the user does not yet have a smart account, you must create one. - -```bash twoslash -ACCOUNT_ADDRESS=$(curl --request POST \ - --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ - --header 'accept: application/json' \ - --data ' -{ - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "'$SIGNER_ADDRESS'" - } - ] -} -' | jq -r '.result.accountAddress') -``` - - - -First prepare the calls. +Prepare the calls using your signer address as the `from` field. With 7702-enabled accounts, you can use your signer address directly without first calling `wallet_requestAccount`. ```bash twoslash PREPARE_CALLS_RESPONSE=$(curl --request POST \ @@ -67,7 +43,7 @@ PREPARE_CALLS_RESPONSE=$(curl --request POST \ "value": "0x00" } ], - "from": "'$ACCOUNT_ADDRESS'", + "from": "'$SIGNER_ADDRESS'", "chainId": "'$CHAIN_ID'" } ] @@ -80,15 +56,40 @@ PREPARE_CALLS_RESPONSE=$(curl --request POST \ Sign the returned signature request. How you do this will vary depending on your environment. +On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation that need to be signed. On subsequent calls, only the user operation signature is needed. + ```bash twoslash # Extract values from prepare calls response CALL_TYPE=$(echo $PREPARE_CALLS_RESPONSE | jq -r .result.type) CALL_DATA=$(echo $PREPARE_CALLS_RESPONSE | jq -r .result.data) -SIGNATURE_PAYLOAD=$(echo $PREPARE_CALLS_RESPONSE | jq -r .result.signatureRequest.data.raw) -# Sign the payload with your private key -SIGNATURE=$(cast wallet sign --private-key "$SIGNER_PRIVATE_KEY" "$SIGNATURE_PAYLOAD") + +if [ "$CALL_TYPE" = "array" ]; then + # First transaction: sign both authorization and user operation + # Sign the 7702 authorization + AUTH_PAYLOAD=$(echo $PREPARE_CALLS_RESPONSE | jq -r '.result.data[0].signatureRequest.rawPayload') + AUTH_SIGNATURE=$(cast wallet sign --no-hash --private-key "$SIGNER_PRIVATE_KEY" "$AUTH_PAYLOAD") + + # Sign the user operation + UO_PAYLOAD=$(echo $PREPARE_CALLS_RESPONSE | jq -r '.result.data[1].signatureRequest.data.raw') + UO_SIGNATURE=$(cast wallet sign --private-key "$SIGNER_PRIVATE_KEY" "$UO_PAYLOAD") + + # Build the signed array data + SIGNED_DATA=$(echo $PREPARE_CALLS_RESPONSE | jq -c --arg auth_sig "$AUTH_SIGNATURE" --arg uo_sig "$UO_SIGNATURE" ' + .result.data | [ + { type: .[0].type, data: .[0].data, chainId: .[0].chainId, signature: { type: "secp256k1", data: $auth_sig } }, + { type: .[1].type, data: .[1].data, chainId: .[1].chainId, signature: { type: "secp256k1", data: $uo_sig } } + ]') +else + # Subsequent transactions: sign only the user operation + SIGNATURE_PAYLOAD=$(echo $PREPARE_CALLS_RESPONSE | jq -r .result.signatureRequest.data.raw) + SIGNATURE=$(cast wallet sign --private-key "$SIGNER_PRIVATE_KEY" "$SIGNATURE_PAYLOAD") +fi ``` + + Using `cast wallet sign --no-hash` for the authorization payload is acceptable for testing but not recommended for production. See [Using EIP-7702](/wallets/transactions/using-eip-7702) for production signing recommendations. + + @@ -96,28 +97,48 @@ SIGNATURE=$(cast wallet sign --private-key "$SIGNER_PRIVATE_KEY" "$SIGNATURE_PAY Send the signed calls using `wallet_sendPreparedCalls`. ```bash twoslash -SEND_CALLS_RESPONSE=$(echo '{}' | jq -n \ - --arg call_type "$CALL_TYPE" \ - --argjson call_data "$CALL_DATA" \ - --arg chain_id "$CHAIN_ID" \ - --arg signature "$SIGNATURE" \ - '{ - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_sendPreparedCalls", - "params": [{ - "type": $call_type, - "data": $call_data, - "chainId": $chain_id, - "signature": { - "type": "secp256k1", - "data": $signature - } - }] - }' | curl --request POST \ - --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ - --header 'accept: application/json' \ - --data @-) +if [ "$CALL_TYPE" = "array" ]; then + # Send the array of signed calls + SEND_CALLS_RESPONSE=$(echo '{}' | jq -n \ + --argjson signed_data "$SIGNED_DATA" \ + --arg chain_id "$CHAIN_ID" \ + '{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_sendPreparedCalls", + "params": [{ + "type": "array", + "data": $signed_data + }] + }' | curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data @-) +else + # Send the single user operation + SEND_CALLS_RESPONSE=$(echo '{}' | jq -n \ + --arg call_type "$CALL_TYPE" \ + --argjson call_data "$CALL_DATA" \ + --arg chain_id "$CHAIN_ID" \ + --arg signature "$SIGNATURE" \ + '{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_sendPreparedCalls", + "params": [{ + "type": $call_type, + "data": $call_data, + "chainId": $chain_id, + "signature": { + "type": "secp256k1", + "data": $signature + } + }] + }' | curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data @-) +fi CALL_ID=$(echo $SEND_CALLS_RESPONSE | jq -r '.result.preparedCallIds[0]') ``` @@ -126,7 +147,7 @@ CALL_ID=$(echo $SEND_CALLS_RESPONSE | jq -r '.result.preparedCallIds[0]') -Finally, use `wallet_getCallsStatus` to check the status of the call. A “pending” state (1xx status codes) is expected for some time before the transition to “confirmed,” so this endpoint should be polled while the status is pending. If the call does not progress, refer to the [retry guide](/wallets/transactions/retry-transactions). The [API documentation](/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-get-calls-status#response.body.status) provides details on each status code. +Finally, use `wallet_getCallsStatus` to check the status of the call. A "pending" state (1xx status codes) is expected for some time before the transition to "confirmed," so this endpoint should be polled while the status is pending. If the call does not progress, refer to the [retry guide](/wallets/transactions/retry-transactions). The [API documentation](/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-get-calls-status#response.body.status) provides details on each status code. ```bash twoslash curl --request POST \ diff --git a/fern/wallets/pages/transactions/send-transactions/client.mdx b/fern/wallets/pages/transactions/send-transactions/client.mdx index 12a9b9590..769fd9f53 100644 --- a/fern/wallets/pages/transactions/send-transactions/client.mdx +++ b/fern/wallets/pages/transactions/send-transactions/client.mdx @@ -28,30 +28,21 @@ console.log(result); ``` ```ts title="client.ts" +import type { Hex } from "viem"; import { LocalAccountSigner } from "@aa-sdk/core"; import { alchemy, sepolia } from "@account-kit/infra"; -import { - createSmartWalletClient, - type SmartWalletClientParams, -} from "@account-kit/wallet-client"; +import { createSmartWalletClient } from "@account-kit/wallet-client"; -const clientParams: SmartWalletClientParams = { +export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, +); + +export const client = createSmartWalletClient({ transport: alchemy({ - apiKey: "your-alchemy-api-key", + apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - "0x-your-wallet-private-key", - ), -}; - -const clientWithoutAccount = createSmartWalletClient(clientParams); - -const account = await clientWithoutAccount.requestAccount(); - -export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, + signer, }); ``` diff --git a/fern/wallets/pages/transactions/send-transactions/encoding-function-data/api.mdx b/fern/wallets/pages/transactions/send-transactions/encoding-function-data/api.mdx index 2c9ba52bb..0c378c357 100644 --- a/fern/wallets/pages/transactions/send-transactions/encoding-function-data/api.mdx +++ b/fern/wallets/pages/transactions/send-transactions/encoding-function-data/api.mdx @@ -14,10 +14,10 @@ curl --request POST \ "calls": [ { "to": "0x0000000000000000000000000000000000000000", - "data": "'$(cast calldata "mintTo(address)" $ACCOUNT_ADDRESS)'" + "data": "'$(cast calldata "mintTo(address)" $SIGNER_ADDRESS)'" } ], - "from": "'$ACCOUNT_ADDRESS'", + "from": "'$SIGNER_ADDRESS'", "chainId": "'$CHAIN_ID'" } ] diff --git a/fern/wallets/pages/transactions/send-transactions/encoding-function-data/client.mdx b/fern/wallets/pages/transactions/send-transactions/encoding-function-data/client.mdx index 2d10496d3..3923cafe1 100644 --- a/fern/wallets/pages/transactions/send-transactions/encoding-function-data/client.mdx +++ b/fern/wallets/pages/transactions/send-transactions/encoding-function-data/client.mdx @@ -34,30 +34,21 @@ export const exampleAbi = [ ``` ```ts title="client.ts" +import type { Hex } from "viem"; import { LocalAccountSigner } from "@aa-sdk/core"; import { alchemy, sepolia } from "@account-kit/infra"; -import { - createSmartWalletClient, - type SmartWalletClientParams, -} from "@account-kit/wallet-client"; +import { createSmartWalletClient } from "@account-kit/wallet-client"; -const clientParams: SmartWalletClientParams = { +export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, +); + +export const client = createSmartWalletClient({ transport: alchemy({ - apiKey: "your-alchemy-api-key", + apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - "0x-your-wallet-private-key", - ), -}; - -const clientWithoutAccount = createSmartWalletClient(clientParams); - -const account = await clientWithoutAccount.requestAccount(); - -export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, + signer, }); ``` diff --git a/fern/wallets/pages/transactions/send-transactions/prepare-calls/client.mdx b/fern/wallets/pages/transactions/send-transactions/prepare-calls/client.mdx index a1f2a3345..2c3eb910e 100644 --- a/fern/wallets/pages/transactions/send-transactions/prepare-calls/client.mdx +++ b/fern/wallets/pages/transactions/send-transactions/prepare-calls/client.mdx @@ -28,30 +28,21 @@ console.log({ sentCalls }); ``` ```ts title="client.ts" +import type { Hex } from "viem"; import { LocalAccountSigner } from "@aa-sdk/core"; import { alchemy, sepolia } from "@account-kit/infra"; -import { - createSmartWalletClient, - type SmartWalletClientParams, -} from "@account-kit/wallet-client"; +import { createSmartWalletClient } from "@account-kit/wallet-client"; -const clientParams: SmartWalletClientParams = { +export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, +); + +export const client = createSmartWalletClient({ transport: alchemy({ - apiKey: "your-alchemy-api-key", + apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - "0x-your-wallet-private-key", - ), -}; - -const clientWithoutAccount = createSmartWalletClient(clientParams); - -const account = await clientWithoutAccount.requestAccount(); - -export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, + signer, }); ``` diff --git a/fern/wallets/pages/transactions/signing/sign-messages/client-raw.mdx b/fern/wallets/pages/transactions/signing/sign-messages/client-raw.mdx index a5cd98af2..15a0dd192 100644 --- a/fern/wallets/pages/transactions/signing/sign-messages/client-raw.mdx +++ b/fern/wallets/pages/transactions/signing/sign-messages/client-raw.mdx @@ -26,23 +26,16 @@ import { LocalAccountSigner } from "@aa-sdk/core"; import { alchemy, sepolia } from "@account-kit/infra"; import { createSmartWalletClient } from "@account-kit/wallet-client"; -const clientParams = { +export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, +); + +export const client = createSmartWalletClient({ transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - process.env.PRIVATE_KEY! as Hex, - ), -}; - -const clientWithoutAccount = createSmartWalletClient(clientParams); - -const account = await clientWithoutAccount.requestAccount(); - -export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, + signer, }); ``` diff --git a/fern/wallets/pages/transactions/signing/sign-messages/client-text.mdx b/fern/wallets/pages/transactions/signing/sign-messages/client-text.mdx index 782330524..182325507 100644 --- a/fern/wallets/pages/transactions/signing/sign-messages/client-text.mdx +++ b/fern/wallets/pages/transactions/signing/sign-messages/client-text.mdx @@ -24,23 +24,16 @@ import { LocalAccountSigner } from "@aa-sdk/core"; import { alchemy, sepolia } from "@account-kit/infra"; import { createSmartWalletClient } from "@account-kit/wallet-client"; -const clientParams = { +export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, +); + +export const client = createSmartWalletClient({ transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - process.env.PRIVATE_KEY! as Hex, - ), -}; - -const clientWithoutAccount = createSmartWalletClient(clientParams); - -const account = await clientWithoutAccount.requestAccount(); - -export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, + signer, }); ``` diff --git a/fern/wallets/pages/transactions/signing/sign-typed-data/client.mdx b/fern/wallets/pages/transactions/signing/sign-typed-data/client.mdx index 23ab5d195..92d183b91 100644 --- a/fern/wallets/pages/transactions/signing/sign-typed-data/client.mdx +++ b/fern/wallets/pages/transactions/signing/sign-typed-data/client.mdx @@ -44,23 +44,16 @@ import { LocalAccountSigner } from "@aa-sdk/core"; import { alchemy, sepolia } from "@account-kit/infra"; import { createSmartWalletClient } from "@account-kit/wallet-client"; -const clientParams = { +export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, +); + +export const client = createSmartWalletClient({ transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - process.env.PRIVATE_KEY! as Hex, - ), -}; - -const clientWithoutAccount = createSmartWalletClient(clientParams); - -const account = await clientWithoutAccount.requestAccount(); - -export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, + signer, }); ``` diff --git a/fern/wallets/pages/transactions/sponsor-gas/api.mdx b/fern/wallets/pages/transactions/sponsor-gas/api.mdx index b49e9037a..387c3b789 100644 --- a/fern/wallets/pages/transactions/sponsor-gas/api.mdx +++ b/fern/wallets/pages/transactions/sponsor-gas/api.mdx @@ -3,28 +3,8 @@ reference](/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/ for full descriptions of the parameters used in the following example. - - If the user does not yet have a smart account, you must create one. -```bash -ACCOUNT_ADDRESS=$(curl --request POST \ - --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ - --header 'accept: application/json' \ - --data ' -{ - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "'$SIGNER_ADDRESS'" - } - ] -} -' | jq -r '.result.accountAddress') -``` - - Prepare calls using the `paymasterService` capability. + Prepare calls using the `paymasterService` capability. Use your signer address directly as the `from` field. ```bash curl --request POST \ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ @@ -46,7 +26,7 @@ curl --request POST \ "to": "0x0000000000000000000000000000000000000000" } ], - "from": "'$ACCOUNT_ADDRESS'", + "from": "'$SIGNER_ADDRESS'", "chainId": "'$CHAIN_ID_HEX'" } ] @@ -54,6 +34,6 @@ curl --request POST \ ``` - Sign the returned signature request and send using `wallet_sendPreparedCalls`. + Sign the returned signature request and send using `wallet_sendPreparedCalls`. See [Send Transactions](/wallets/transactions/send-transactions) for a complete example including handling first-time 7702 authorization. diff --git a/fern/wallets/pages/transactions/sponsor-gas/client.mdx b/fern/wallets/pages/transactions/sponsor-gas/client.mdx index bb0b98325..85ca2dddf 100644 --- a/fern/wallets/pages/transactions/sponsor-gas/client.mdx +++ b/fern/wallets/pages/transactions/sponsor-gas/client.mdx @@ -44,23 +44,16 @@ export const config = { policyId: process.env.ALCHEMY_POLICY_ID!, }; -const clientParams = { +export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, +); + +export const client = createSmartWalletClient({ transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - process.env.PRIVATE_KEY! as Hex, - ), -}; - -const clientWithoutAccount = createSmartWalletClient(clientParams); - -const account = await clientWithoutAccount.requestAccount(); - -export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, + signer, }); ``` diff --git a/fern/wallets/pages/transactions/swap-tokens/api.mdx b/fern/wallets/pages/transactions/swap-tokens/api.mdx index 37c61a02b..6f22d63a1 100644 --- a/fern/wallets/pages/transactions/swap-tokens/api.mdx +++ b/fern/wallets/pages/transactions/swap-tokens/api.mdx @@ -2,43 +2,9 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. - - -```bash -curl -X POST https://api.g.alchemy.com/v2/{apiKey} \ - -H "Content-Type: application/json" \ - -d '{ - "jsonrpc": "2.0", - "id": 1, - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "{SIGNER_ADDRESS}" - } - ] - }' -``` - -This returns: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "accountAddress": "ACCOUNT_ADDRESS", - "id": "ACCOUNT_ID" - } -} -``` - -For other potential responses, [check out the API reference!](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account) - - - -Note that `postCalls` are optional and allow you to batch an array of calls after the swap. +Use your signer address directly as the `from` field. Note that `postCalls` are optional and allow you to batch an array of calls after the swap. If you're using an EOA or just want the raw array of calls returned, pass the @@ -55,7 +21,7 @@ curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ "method": "wallet_requestQuote_v0", "params": [ { - "from": "{ACCOUNT_ADDRESS_FROM_STEP_1}", + "from": "{SIGNER_ADDRESS}", "chainId": "{CHAIN_ID}", "fromToken": "{FROM_TOKEN}", "toToken": "{TO_TOKEN}", @@ -107,15 +73,17 @@ This returns: } ``` -Note the `signatureRequest`! This is what you now have to sign, and the returned `data` field is what you will need to send the transaction! +Note the `signatureRequest`! This is what you now have to sign, and the returned `data` field is what you will need to send the transaction. + +On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for handling first-time authorization signing. To sign the signature request, you should sign the `raw` field (note, this is not a string! You need to pass it to your signer as raw bytes, generally like so: `{ raw: "0x..." }`) with your signer of choice. - + This should use the `personal_sign` RPC method, as noted by the `type` in the `signatureRequest`. - + Alternatively, you can sign the raw payload with a simple `eth_sign` but this RPC method is not favored due to security concerns. @@ -131,11 +99,11 @@ curl -X POST https://api.g.alchemy.com/v2/{apiKey} \ "params": [ { "type": "user-operation-v070", - "data": "{DATA_FROM_STEP_2}", + "data": "{DATA_FROM_STEP_1}", "chainId": "{CHAIN_ID}", "signature": { "type": "secp256k1", - "data": "{SIGNATURE_FROM_STEP_3}" + "data": "{SIGNATURE_FROM_STEP_2}" } } ], @@ -173,7 +141,7 @@ curl -X POST https://api.g.alchemy.com/v2/{apiKey} \ "method": "wallet_getCallsStatus", "params": [ [ - "{PREPARED_CALL_ID_FROM_STEP_4}" + "{PREPARED_CALL_ID_FROM_STEP_3}" ] ], "id": 1 diff --git a/fern/wallets/pages/transactions/swap-tokens/client.mdx b/fern/wallets/pages/transactions/swap-tokens/client.mdx index a7cf1df2d..eb81a07a8 100644 --- a/fern/wallets/pages/transactions/swap-tokens/client.mdx +++ b/fern/wallets/pages/transactions/swap-tokens/client.mdx @@ -9,16 +9,14 @@ You'll need the following env variables: ```ts title="requestQuote.ts" import { swapActions } from "@account-kit/wallet-client/experimental"; - import { client } from "./client"; - - const account = await client.requestAccount(); + import { client, signer } from "./client"; // Add the swap actions to the client const swapClient = client.extend(swapActions); // Request the swap quote const { quote, ...calls } = await swapClient.requestQuoteV0({ - from: account.address, + from: await signer.getAddress(), // Your wallet address fromToken: "0x...", toToken: "0x...", minimumToAmount: "0x...", @@ -69,24 +67,17 @@ You'll need the following env variables: import { alchemy, sepolia } from "@account-kit/infra"; import { createSmartWalletClient } from "@account-kit/wallet-client"; - const clientParams = { + export const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, + ); + + export const client = createSmartWalletClient({ transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY!, }), chain: sepolia, - signer: LocalAccountSigner.privateKeyToAccountSigner( - process.env.PRIVATE_KEY! as Hex, - ), + signer, policyId: process.env.ALCHEMY_POLICY_ID!, // Optional: If you're using a gas manager policy - }; - - const clientWithoutAccount = createSmartWalletClient(clientParams); - - const account = await clientWithoutAccount.requestAccount(); - - export const client = createSmartWalletClient({ - ...clientParams, - account: account.address, }); ``` diff --git a/fern/wallets/pages/transactions/using-eip-7702/api.mdx b/fern/wallets/pages/transactions/using-eip-7702/api.mdx index ff6f42aae..f2385527a 100644 --- a/fern/wallets/pages/transactions/using-eip-7702/api.mdx +++ b/fern/wallets/pages/transactions/using-eip-7702/api.mdx @@ -2,9 +2,11 @@ See the [`wallet_prepareCalls` API reference](/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls) for full descriptions of the parameters used in the following example. +EIP-7702 is now the default mode when using your signer address directly with `wallet_prepareCalls`. The API will automatically return authorization requests when delegation is needed. + - Prepare calls using the `eip7702Auth` capability. + Prepare calls using your signer address directly as the `from` field. ```bash curl --request POST \ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ @@ -19,8 +21,7 @@ curl --request POST \ "capabilities": { "paymasterService": { "policyId": "'$ALCHEMY_POLICY_ID'" - }, - "eip7702Auth": true + } }, "calls": [ { @@ -35,7 +36,8 @@ curl --request POST \ ``` - Sign the returned signature request and send using `wallet_sendPreparedCalls`. - See "Usage with prepare calls" for details on the authorization signature request. + Sign the returned signature request(s) and send using `wallet_sendPreparedCalls`. + + On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for a complete example including handling the authorization signature. diff --git a/fern/wallets/pages/transactions/using-eip-7702/client.mdx b/fern/wallets/pages/transactions/using-eip-7702/client.mdx index 3ebaa59e4..8070837ce 100644 --- a/fern/wallets/pages/transactions/using-eip-7702/client.mdx +++ b/fern/wallets/pages/transactions/using-eip-7702/client.mdx @@ -1,7 +1,6 @@ Required SDK version: ^v4.61.0 -Use the `eip7702Auth` capability on the smart wallet client `sendCalls` action. `sendCalls` will automatically -sign any required authorization requests to delegate the EOA to Modular Account v2. +EIP-7702 is now the default mode for Smart Wallets. Simply create a client with your signer and use `sendCalls` - the SDK will automatically handle authorization requests to delegate the EOA to Modular Account v2 when needed. If the signing EOA is delegated to a different smart contract, `sendCalls` @@ -24,7 +23,6 @@ try { paymasterService: { policyId: config.policyId, }, - eip7702Auth: true, }, calls: [ { @@ -52,10 +50,9 @@ export const config = { policyId: process.env.ALCHEMY_POLICY_ID!, }; -const signer = LocalAccountSigner.privateKeyToAccountSigner( +export const signer = LocalAccountSigner.privateKeyToAccountSigner( process.env.PRIVATE_KEY! as Hex, ); -const signerAddress = await signer.getAddress(); export const client = createSmartWalletClient({ transport: alchemy({ @@ -63,8 +60,6 @@ export const client = createSmartWalletClient({ }), chain: sepolia, signer, - // hoist the signer address as the account when using 7702 mode - account: signerAddress, }); ``` diff --git a/fern/wallets/pages/transactions/using-eip-7702/index.mdx b/fern/wallets/pages/transactions/using-eip-7702/index.mdx index cc4dd55c0..161f96483 100644 --- a/fern/wallets/pages/transactions/using-eip-7702/index.mdx +++ b/fern/wallets/pages/transactions/using-eip-7702/index.mdx @@ -1,22 +1,22 @@ --- -title: Using EIP-7702 -description: Upgrade to Smart Wallets with EIP-7702 +title: How EIP-7702 Works +description: Understanding how Smart Wallets use EIP-7702 slug: wallets/transactions/using-eip-7702 --- -Upgrade your user's accounts to Smart Wallets using [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702). -This gives users access to all of the capabilities of Smart Wallets including gas sponsorship, batching, and more. +Alchemy Smart Wallets use [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) by default to give users access to all Smart Wallet capabilities including gas sponsorship, batching, and more - while keeping the same address. ## How it works EIP-7702 enables EOAs (Externally Owned Accounts) to delegate control to Smart Wallets that can execute code directly from their addresses. When using Transaction APIs: -* Transaction APIs detect whether a user must first delegate via EIP-7702 +* You can use your signer address directly as the account address - no need to call `wallet_requestAccount` first +* Transaction APIs automatically detect whether a user must first delegate via EIP-7702 * If delegation is required, Transaction APIs prepare the correct authorization payload * Your application prompts the user to sign when required * We combine the delegation and transaction into a single onchain submission -Once delegated, the user’s account behaves as a Smart Wallet while keeping the same address and assets. +Once delegated, the user's account behaves as a Smart Wallet while keeping the same address and assets. Subsequent transactions only require a single user operation signature. ## Prerequisites @@ -102,13 +102,14 @@ Once delegated, the user’s account behaves as a Smart Wallet while keeping the + EIP-7702 delegation is now the default mode for Alchemy Smart Wallets. When you use your signer address directly with `wallet_prepareCalls` or other Transaction APIs, 7702 mode is automatically enabled. + The `eip7702Auth` capability supports the interface defined in [ERC-7902](https://eips.ethereum.org/EIPS/eip-7902). Currently, Wallet APIs only support delegation to the following contract: `0x69007702764179f14F51cdce752f4f775d74E139` (Modular Account v2) All other delegation addresses will be rejected. - To simplify usage, it is recommended to use `eip7702Auth: true`. Once delegated, an account remains delegated until the delegation is replaced or removed. @@ -121,6 +122,80 @@ Once delegated, the user’s account behaves as a Smart Wallet while keeping the + + If you need to use a traditional Smart Contract Account instead of EIP-7702, you can opt out of the default 7702 behavior by calling `wallet_requestAccount` first. + + When you call `wallet_requestAccount` with a signer address, it creates a dedicated Smart Contract Account address. Using this SCA address in subsequent API calls will bypass 7702 mode. + + **SDK Example:** + ```ts + import { createSmartWalletClient } from "@account-kit/wallet-client"; + import { LocalAccountSigner } from "@aa-sdk/core"; + import { alchemy, sepolia } from "@account-kit/infra"; + + const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY!, + ); + + // Create client without account first + const clientWithoutAccount = createSmartWalletClient({ + transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY! }), + chain: sepolia, + signer, + }); + + // Request a Smart Contract Account + const account = await clientWithoutAccount.requestAccount(); + + // Create a new client with the SCA address + const client = createSmartWalletClient({ + transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY! }), + chain: sepolia, + signer, + account: account.address, // Use the SCA address, not the signer address + }); + + // Transactions will now use the SCA instead of 7702 + await client.sendCalls({ calls: [...] }); + ``` + + **API Example:** + ```bash + # First, request a Smart Contract Account + ACCOUNT_ADDRESS=$(curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data '{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_requestAccount", + "params": [{ "signerAddress": "'$SIGNER_ADDRESS'" }] + }' | jq -r '.result.accountAddress') + + # Use the SCA address (not the signer address) in subsequent calls + curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data '{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_prepareCalls", + "params": [{ + "calls": [{ "to": "0x...", "data": "0x" }], + "from": "'$ACCOUNT_ADDRESS'", + "chainId": "'$CHAIN_ID'" + }] + }' + ``` + + **When to use SCA mode:** + * When you need backwards compatibility with existing Smart Contract Accounts + * When working with chains that don't yet support EIP-7702 + * When you have specific requirements for smart contract account architecture + + Note: If an existing non-7702 account is found in our database for a given signer, the system will automatically use that account even when you pass the signer address directly. + + ## Next steps Build more: From 188d8904504d6a46c0a3a4aed64bcdbbf244be8e Mon Sep 17 00:00:00 2001 From: jakehobbs Date: Thu, 22 Jan 2026 16:39:16 -0800 Subject: [PATCH 2/7] docs: second pass of 7702 default --- .../cross-chain-swap-tokens/api.mdx | 2 +- .../pay-gas-with-any-token/api-postop.mdx | 4 ++-- .../pay-gas-with-any-token/api-preop.mdx | 4 ++-- .../transactions/retry-transactions/api.mdx | 4 ++-- .../send-batch-transactions/api.mdx | 2 +- .../send-parallel-transactions/api.mdx | 4 +++- .../transactions/send-transactions/api.mdx | 2 +- .../pages/transactions/sponsor-gas/api.mdx | 2 +- .../pages/transactions/swap-tokens/api.mdx | 2 +- .../pages/transactions/using-eip-7702/api.mdx | 2 +- .../transactions/using-eip-7702/index.mdx | 20 ++++++------------- 11 files changed, 21 insertions(+), 27 deletions(-) diff --git a/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx b/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx index 518ba6782..65e076543 100644 --- a/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx +++ b/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx @@ -9,7 +9,7 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. additional actions after a cross-chain swap. -Use your signer address directly as the `from` field. Request a cross-chain swap quote by specifying both the source chain (`chainId`) and destination chain (`toChainId`). In addition, just like in [single-chain swaps](/wallets/transactions/swap-tokens) you can specify either a `minimumToAmount` or a `fromAmount`. +Use your signer address directly as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. Request a cross-chain swap quote by specifying both the source chain (`chainId`) and destination chain (`toChainId`). In addition, just like in [single-chain swaps](/wallets/transactions/swap-tokens) you can specify either a `minimumToAmount` or a `fromAmount`. If you're using an EOA or just want the raw array of calls returned, pass the diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx index fca20dd1b..1ff6f2ba1 100644 --- a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx +++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx @@ -4,7 +4,7 @@ for full descriptions of the parameters used in the following example. - Prepare calls using the `paymasterService` capability. Use your signer address directly as the `from` field. + Prepare calls using the `paymasterService` capability. Use your signer address directly as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. ```bash curl --request POST \ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ @@ -43,6 +43,6 @@ curl --request POST \ ``` - Sign the returned signature request and send using `wallet_sendPreparedCalls`. See the [send transactions guide](/wallets/transactions/send-transactions) for more info. + Sign the returned signature request and send using `wallet_sendPreparedCalls`. On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See the [send transactions guide](/wallets/transactions/send-transactions) for a complete example. diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx index 003e68d97..0d0afe2a8 100644 --- a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx +++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx @@ -1,6 +1,6 @@ - Prepare calls using the `paymasterService` capability. Use your signer address directly as the `from` field. + Prepare calls using the `paymasterService` capability. Use your signer address directly as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. ```bash curl --request POST \ @@ -74,6 +74,6 @@ - Sign and send the last prepared calls result as usual using `wallet_sendPreparedCalls`. See the [send transactions guide](/wallets/transactions/send-transactions) for more info. + Sign and send the last prepared calls result as usual using `wallet_sendPreparedCalls`. On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See the [send transactions guide](/wallets/transactions/send-transactions) for a complete example. diff --git a/fern/wallets/pages/transactions/retry-transactions/api.mdx b/fern/wallets/pages/transactions/retry-transactions/api.mdx index 43fed267a..843b7f1fa 100644 --- a/fern/wallets/pages/transactions/retry-transactions/api.mdx +++ b/fern/wallets/pages/transactions/retry-transactions/api.mdx @@ -4,7 +4,7 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. -Use your signer address directly as the `from` field. +Use your signer address directly as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. ```bash curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ @@ -55,7 +55,7 @@ This returns: } ``` -Sign the `raw` field and send the transaction as usual. See the [sending transactions documentation](/wallets/transactions/send-transactions) for more details. +Sign the `raw` field and send the transaction as usual. On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See the [sending transactions documentation](/wallets/transactions/send-transactions) for more details. diff --git a/fern/wallets/pages/transactions/send-batch-transactions/api.mdx b/fern/wallets/pages/transactions/send-batch-transactions/api.mdx index cc6be62a2..38afdda2b 100644 --- a/fern/wallets/pages/transactions/send-batch-transactions/api.mdx +++ b/fern/wallets/pages/transactions/send-batch-transactions/api.mdx @@ -9,7 +9,7 @@ for full descriptions of the parameters used in the following example. - Prepare calls with multiple calls specified in the batch. Use your signer address directly as the `from` field. + Prepare calls with multiple calls specified in the batch. Use your signer address directly as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. ```bash twoslash curl --request POST \ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ diff --git a/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx b/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx index 651b0bc2b..416475259 100644 --- a/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx +++ b/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx @@ -2,7 +2,7 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. - Use your signer address directly as the `from` field. To send parallel calls, use the `nonceOverride` capability. This allows us to specify a `nonceKey` parameter, a hex value that fits inside a `uint152` and differentiates transactions. + Use your signer address directly as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. To send parallel calls, use the `nonceOverride` capability. This allows us to specify a `nonceKey` parameter, a hex value that fits inside a `uint152` and differentiates transactions. In production, as long as the nonce key override is nonzero (zero is the default), and different between `prepareCalls` requests, the transactions will be processed in parallel. @@ -90,6 +90,8 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. ``` Note the two `signatureRequest` objects that were returned for each request! Also keep the returned `data` object from each request, you'll need them when we send the parallel transactions! + + On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for handling first-time authorization signing. diff --git a/fern/wallets/pages/transactions/send-transactions/api.mdx b/fern/wallets/pages/transactions/send-transactions/api.mdx index 8f029e3bb..616a85e5e 100644 --- a/fern/wallets/pages/transactions/send-transactions/api.mdx +++ b/fern/wallets/pages/transactions/send-transactions/api.mdx @@ -23,7 +23,7 @@ SIGNER_PRIVATE_KEY=your-signer-private-key -Prepare the calls using your signer address as the `from` field. With 7702-enabled accounts, you can use your signer address directly without first calling `wallet_requestAccount`. +Prepare the calls using your signer address as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. You can use your signer address directly without first calling `wallet_requestAccount`. ```bash twoslash PREPARE_CALLS_RESPONSE=$(curl --request POST \ diff --git a/fern/wallets/pages/transactions/sponsor-gas/api.mdx b/fern/wallets/pages/transactions/sponsor-gas/api.mdx index 387c3b789..639ce75cf 100644 --- a/fern/wallets/pages/transactions/sponsor-gas/api.mdx +++ b/fern/wallets/pages/transactions/sponsor-gas/api.mdx @@ -4,7 +4,7 @@ for full descriptions of the parameters used in the following example. - Prepare calls using the `paymasterService` capability. Use your signer address directly as the `from` field. + Prepare calls using the `paymasterService` capability. Use your signer address directly as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. ```bash curl --request POST \ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ diff --git a/fern/wallets/pages/transactions/swap-tokens/api.mdx b/fern/wallets/pages/transactions/swap-tokens/api.mdx index 6f22d63a1..b99f58121 100644 --- a/fern/wallets/pages/transactions/swap-tokens/api.mdx +++ b/fern/wallets/pages/transactions/swap-tokens/api.mdx @@ -4,7 +4,7 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. -Use your signer address directly as the `from` field. Note that `postCalls` are optional and allow you to batch an array of calls after the swap. +Use your signer address directly as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. Note that `postCalls` are optional and allow you to batch an array of calls after the swap. If you're using an EOA or just want the raw array of calls returned, pass the diff --git a/fern/wallets/pages/transactions/using-eip-7702/api.mdx b/fern/wallets/pages/transactions/using-eip-7702/api.mdx index f2385527a..cf87bdded 100644 --- a/fern/wallets/pages/transactions/using-eip-7702/api.mdx +++ b/fern/wallets/pages/transactions/using-eip-7702/api.mdx @@ -2,7 +2,7 @@ See the [`wallet_prepareCalls` API reference](/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls) for full descriptions of the parameters used in the following example. -EIP-7702 is now the default mode when using your signer address directly with `wallet_prepareCalls`. The API will automatically return authorization requests when delegation is needed. +EIP-7702 is now the default mode when using your signer address directly with `wallet_prepareCalls`. The API will automatically return an authorization request when delegation is needed. diff --git a/fern/wallets/pages/transactions/using-eip-7702/index.mdx b/fern/wallets/pages/transactions/using-eip-7702/index.mdx index 161f96483..c9ec873f1 100644 --- a/fern/wallets/pages/transactions/using-eip-7702/index.mdx +++ b/fern/wallets/pages/transactions/using-eip-7702/index.mdx @@ -137,26 +137,20 @@ Once delegated, the user's account behaves as a Smart Wallet while keeping the s process.env.PRIVATE_KEY!, ); - // Create client without account first - const clientWithoutAccount = createSmartWalletClient({ + const client = createSmartWalletClient({ transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY! }), chain: sepolia, signer, }); // Request a Smart Contract Account - const account = await clientWithoutAccount.requestAccount(); + const account = await client.requestAccount(); - // Create a new client with the SCA address - const client = createSmartWalletClient({ - transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY! }), - chain: sepolia, - signer, - account: account.address, // Use the SCA address, not the signer address + // Use the SCA address as the `from` param to bypass 7702 mode + await client.sendCalls({ + from: account.address, + calls: [...], }); - - // Transactions will now use the SCA instead of 7702 - await client.sendCalls({ calls: [...] }); ``` **API Example:** @@ -192,8 +186,6 @@ Once delegated, the user's account behaves as a Smart Wallet while keeping the s * When you need backwards compatibility with existing Smart Contract Accounts * When working with chains that don't yet support EIP-7702 * When you have specific requirements for smart contract account architecture - - Note: If an existing non-7702 account is found in our database for a given signer, the system will automatically use that account even when you pass the signer address directly. ## Next steps From ea92b9677a9ff8f6ec6b08d9940c421c52f731f4 Mon Sep 17 00:00:00 2001 From: jakehobbs Date: Mon, 26 Jan 2026 16:13:28 -0800 Subject: [PATCH 3/7] fix(docs): fix txn sdk examples --- .../smart-wallets/session-keys/index.mdx | 4 +++- .../pages/smart-wallets/session-keys/sdk.mdx | 20 ++++++++++--------- .../pay-gas-with-any-token/client.mdx | 3 ++- .../retry-transactions/client.mdx | 10 +++++----- .../send-batch-transactions/client.mdx | 3 ++- .../send-parallel-transactions/client.mdx | 6 +++++- .../transactions/send-transactions/client.mdx | 3 ++- .../encoding-function-data/client.mdx | 7 ++++--- .../prepare-calls/client.mdx | 5 +++-- .../signing/sign-messages/client-raw.mdx | 3 ++- .../signing/sign-messages/client-text.mdx | 3 ++- .../signing/sign-typed-data/client.mdx | 11 +++++++--- .../pages/transactions/sponsor-gas/client.mdx | 3 ++- .../transactions/using-eip-7702/client.mdx | 3 ++- 14 files changed, 53 insertions(+), 31 deletions(-) diff --git a/fern/wallets/pages/smart-wallets/session-keys/index.mdx b/fern/wallets/pages/smart-wallets/session-keys/index.mdx index 2daef1780..7da2b5580 100644 --- a/fern/wallets/pages/smart-wallets/session-keys/index.mdx +++ b/fern/wallets/pages/smart-wallets/session-keys/index.mdx @@ -37,8 +37,10 @@ We'll demonstrate how to create and use session keys [using the SDK client](/doc To specify permissions during a session key installation, include them in the `permissions` array when calling `client.grantPermission()` via the SDK or `wallet_createSession` via the API. ```ts +const signerAddress = await signer.getAddress(); + const permissions = await client.grantPermissions({ - account: account.address, + account: signerAddress, expirySec: Math.floor(Date.now() / 1000) + 60 * 60, key: { publicKey: await sessionKey.getAddress(), diff --git a/fern/wallets/pages/smart-wallets/session-keys/sdk.mdx b/fern/wallets/pages/smart-wallets/session-keys/sdk.mdx index c88acd114..bec4edcd3 100644 --- a/fern/wallets/pages/smart-wallets/session-keys/sdk.mdx +++ b/fern/wallets/pages/smart-wallets/session-keys/sdk.mdx @@ -52,17 +52,19 @@ const client = createSmartWalletClient({ ### 3. Create the session key - + -If you are using an EIP-7702 account, the account must be delegated onchain -before creating the session. If the account has already sent calls, it will -already be delegated. If it hasn't sent any calls before creating the session -key, you can delegate it by sending an empty call as the owner: +EIP-7702 accounts must be delegated onchain before creating a session. If the +account has already sent calls, it will already be delegated. If it hasn't sent +any calls before creating the session key, you can delegate it by sending an +empty call as the owner: ```ts +const signerAddress = await signer.getAddress(); + const preparedCalls = await client.prepareCalls({ calls: [], // empty array since you don't want to send any calls - from: account.address, + from: signerAddress, capabilities: { paymasterService: { policyId: GAS_MANAGER_POLICY_ID, // put your gas manager policy ID here @@ -82,13 +84,13 @@ Now you can continue to create the session key as described below. ```ts -const account = await client.requestAccount(); // Request an account for the owner signer +const signerAddress = await signer.getAddress(); // This is where you would use your session key signer! const sessionKey = LocalAccountSigner.generatePrivateKeySigner(); const permissions = await client.grantPermissions({ - account: account.address, + account: signerAddress, expirySec: Math.floor(Date.now() / 1000) + 60 * 60, key: { publicKey: await sessionKey.getAddress(), @@ -105,7 +107,7 @@ import { signPreparedCalls } from "@account-kit/wallet-client"; const preparedCalls = await client.prepareCalls({ calls: [{ to: "0x0000000000000000000000000000000000000000", value: "0x0" }], - from: account.address, + from: signerAddress, capabilities: { paymasterService: { policyId: GAS_MANAGER_POLICY_ID, // put your gas manager policy ID here diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/client.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/client.mdx index 6ce002f22..17fc6e88b 100644 --- a/fern/wallets/pages/transactions/pay-gas-with-any-token/client.mdx +++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/client.mdx @@ -9,9 +9,10 @@ Use the `paymasterService` capability on the smart wallet client `sendCalls` or ```ts title="sendCalls.ts" -import { client, config } from "./client.ts"; +import { client, config, signer } from "./client.ts"; const { preparedCallIds } = await client.sendCalls({ + from: await signer.getAddress(), capabilities: { paymasterService: { policyId: config.policyId, diff --git a/fern/wallets/pages/transactions/retry-transactions/client.mdx b/fern/wallets/pages/transactions/retry-transactions/client.mdx index 8fd263d58..9e62d379c 100644 --- a/fern/wallets/pages/transactions/retry-transactions/client.mdx +++ b/fern/wallets/pages/transactions/retry-transactions/client.mdx @@ -14,10 +14,11 @@ You'll need the following env variables: ```ts title="retryTransaction.ts" - import { client, config } from "./client.ts"; + import { client, config, signer } from "./client.ts"; // Send initial transaction const { preparedCallIds } = await client.sendCalls({ + from: await signer.getAddress(), capabilities: { paymasterService: { policyId: config.policyId, @@ -35,9 +36,7 @@ You'll need the following env variables: console.log("Initial transaction:", preparedCallIds[0]); // Check status - const status = await client.getCallsStatus({ - id: preparedCallIds[0]!, - }); + const status = await client.getCallsStatus(preparedCallIds[0]!); console.log("Transaction status:", status.status); @@ -47,6 +46,7 @@ You'll need the following env variables: // Re-send without nonceOverride to replace stuck transaction const { preparedCallIds: replacementIds } = await client.sendCalls({ + from: await signer.getAddress(), capabilities: { paymasterService: { policyId: config.policyId, @@ -64,7 +64,7 @@ You'll need the following env variables: console.log("Replacement transaction:", replacementIds[0]); // Wait for replacement to confirm - const result = await client.waitForCallsStatus(replacementIds[0]!); + const result = await client.waitForCallsStatus({ id: replacementIds[0]! }); console.log("Replacement confirmed:", result); } diff --git a/fern/wallets/pages/transactions/send-batch-transactions/client.mdx b/fern/wallets/pages/transactions/send-batch-transactions/client.mdx index 56b5a2592..5ac1bbe7a 100644 --- a/fern/wallets/pages/transactions/send-batch-transactions/client.mdx +++ b/fern/wallets/pages/transactions/send-batch-transactions/client.mdx @@ -10,7 +10,7 @@ You can batch transactions using either the smart wallet client `sendCalls` or ` ```ts title="sendCalls.ts" import { type Address, erc20Abi, encodeFunctionData, parseUnits } from "viem"; -import { client } from "./client"; +import { client, signer } from "./client"; import { swapAbi } from "./swapAbi"; const DEMO_USDC_ADDRESS: Address = "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443"; @@ -18,6 +18,7 @@ const DEMO_USDC_ADDRESS: Address = "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443"; const DEMO_SWAP_ADDRESS: Address = "0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C"; const { preparedCallIds } = await client.sendCalls({ + from: await signer.getAddress(), calls: [ // To batch, simply specify multiple calls in the calls array { diff --git a/fern/wallets/pages/transactions/send-parallel-transactions/client.mdx b/fern/wallets/pages/transactions/send-parallel-transactions/client.mdx index 023ec271d..344d6e695 100644 --- a/fern/wallets/pages/transactions/send-parallel-transactions/client.mdx +++ b/fern/wallets/pages/transactions/send-parallel-transactions/client.mdx @@ -14,13 +14,16 @@ You'll need the following env variables: ```ts title="sendParallelCalls.ts" - import { client, config } from "./client.ts"; + import { client, config, signer } from "./client.ts"; + + const signerAddress = await signer.getAddress(); const [ { preparedCallIds: preparedCallIdsOne }, { preparedCallIds: preparedCallIdsTwo }, ] = await Promise.all([ client.sendCalls({ + from: signerAddress, capabilities: { paymasterService: { policyId: config.policyId, @@ -38,6 +41,7 @@ You'll need the following env variables: ], }), client.sendCalls({ + from: signerAddress, capabilities: { paymasterService: { policyId: config.policyId, diff --git a/fern/wallets/pages/transactions/send-transactions/client.mdx b/fern/wallets/pages/transactions/send-transactions/client.mdx index 769fd9f53..cc5abecc9 100644 --- a/fern/wallets/pages/transactions/send-transactions/client.mdx +++ b/fern/wallets/pages/transactions/send-transactions/client.mdx @@ -8,9 +8,10 @@ You can send transactions using the smart wallet client `sendCalls` action. ```ts title="sendCalls.ts" import { parseEther, toHex } from "viem"; -import { client } from "./client.ts"; +import { client, signer } from "./client.ts"; const { preparedCallIds } = await client.sendCalls({ + from: await signer.getAddress(), calls: [ { to: "0x0000000000000000000000000000000000000000", diff --git a/fern/wallets/pages/transactions/send-transactions/encoding-function-data/client.mdx b/fern/wallets/pages/transactions/send-transactions/encoding-function-data/client.mdx index 3923cafe1..116d54228 100644 --- a/fern/wallets/pages/transactions/send-transactions/encoding-function-data/client.mdx +++ b/fern/wallets/pages/transactions/send-transactions/encoding-function-data/client.mdx @@ -2,19 +2,20 @@ In JavaScript, you can use [Viem](https://viem.sh/docs/contract/encodeFunctionDa -```ts title="sendCalls.ts" focus={9-13} +```ts title="sendCalls.ts" focus={9-14} import { encodeFunctionData } from "viem"; -import { client } from "./client.ts"; +import { client, signer } from "./client.ts"; import { exampleAbi } from "./abi.ts"; await client.sendCalls({ + from: await signer.getAddress(), calls: [ { to: "0x0000000000000000000000000000000000000000", data: encodeFunctionData({ abi: exampleAbi, functionName: "mintTo", - args: [client.account!.address], + args: ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"], }), }, ], diff --git a/fern/wallets/pages/transactions/send-transactions/prepare-calls/client.mdx b/fern/wallets/pages/transactions/send-transactions/prepare-calls/client.mdx index 2c3eb910e..0db03f96b 100644 --- a/fern/wallets/pages/transactions/send-transactions/prepare-calls/client.mdx +++ b/fern/wallets/pages/transactions/send-transactions/prepare-calls/client.mdx @@ -4,11 +4,12 @@ You can use smart wallet client actions to prepare, sign, and send transactions. -```ts title="sendCalls.ts" focus={4-20} +```ts title="sendCalls.ts" focus={4-21} import { parseEther, toHex } from "viem"; -import { client } from "./client.ts"; +import { client, signer } from "./client.ts"; const preparedCalls = await client.prepareCalls({ + from: await signer.getAddress(), calls: [ { to: "0x0000000000000000000000000000000000000000", diff --git a/fern/wallets/pages/transactions/signing/sign-messages/client-raw.mdx b/fern/wallets/pages/transactions/signing/sign-messages/client-raw.mdx index 15a0dd192..2279e4a1e 100644 --- a/fern/wallets/pages/transactions/signing/sign-messages/client-raw.mdx +++ b/fern/wallets/pages/transactions/signing/sign-messages/client-raw.mdx @@ -7,13 +7,14 @@ for full descriptions of the parameters used in the following example. ```ts title="signRawMessage.ts" -import { client } from "./client"; +import { client, signer } from "./client"; // Sign a raw hex message const rawSignature = await client.signMessage({ message: { raw: "0x48656c6c6f2c20776f726c6421", // "Hello, world!" in hex }, + account: await signer.getAddress(), }); console.log("Signature over raw data:", rawSignature); diff --git a/fern/wallets/pages/transactions/signing/sign-messages/client-text.mdx b/fern/wallets/pages/transactions/signing/sign-messages/client-text.mdx index 182325507..75c709874 100644 --- a/fern/wallets/pages/transactions/signing/sign-messages/client-text.mdx +++ b/fern/wallets/pages/transactions/signing/sign-messages/client-text.mdx @@ -7,11 +7,12 @@ for full descriptions of the parameters used in the following example. ```ts title="signTextMessage.ts" -import { client } from "./client"; +import { client, signer } from "./client"; // Sign a simple text message const textSignature = await client.signMessage({ message: "Hello, world!", + account: await signer.getAddress(), }); console.log("Signature over text:", textSignature); diff --git a/fern/wallets/pages/transactions/signing/sign-typed-data/client.mdx b/fern/wallets/pages/transactions/signing/sign-typed-data/client.mdx index 92d183b91..e25a3f3a8 100644 --- a/fern/wallets/pages/transactions/signing/sign-typed-data/client.mdx +++ b/fern/wallets/pages/transactions/signing/sign-typed-data/client.mdx @@ -7,7 +7,9 @@ for full descriptions of the parameters used in the following example. ```ts title="signTypedData.ts" -import { client } from "./client"; +import { client, signer } from "./client"; + +const signerAddress = await signer.getAddress(); // Sign typed data const typedData = { @@ -26,13 +28,16 @@ const typedData = { }, primaryType: "Auth", message: { - user: "0x...", // wallet address + user: signerAddress, nonce: 12345, timestamp: Date.now(), }, } as const; -const signature = await client.signTypedData(typedData); +const signature = await client.signTypedData({ + ...typedData, + account: signerAddress, +}); console.log("Typed data signature:", signature); ``` diff --git a/fern/wallets/pages/transactions/sponsor-gas/client.mdx b/fern/wallets/pages/transactions/sponsor-gas/client.mdx index 85ca2dddf..44ff927e2 100644 --- a/fern/wallets/pages/transactions/sponsor-gas/client.mdx +++ b/fern/wallets/pages/transactions/sponsor-gas/client.mdx @@ -9,10 +9,11 @@ Use the `paymasterService` capability on the smart wallet client `sendCalls` or ```ts title="sendCalls.ts" -import { client, config } from "./client.ts"; +import { client, config, signer } from "./client.ts"; try { const { preparedCallIds } = await client.sendCalls({ + from: await signer.getAddress(), capabilities: { paymasterService: { policyId: config.policyId, diff --git a/fern/wallets/pages/transactions/using-eip-7702/client.mdx b/fern/wallets/pages/transactions/using-eip-7702/client.mdx index 8070837ce..e4404ef30 100644 --- a/fern/wallets/pages/transactions/using-eip-7702/client.mdx +++ b/fern/wallets/pages/transactions/using-eip-7702/client.mdx @@ -15,10 +15,11 @@ for full descriptions of the parameters used in the following example. ```tsx title="sendCalls.ts" -import { client, config } from "./client.ts"; +import { client, config, signer } from "./client.ts"; try { const { preparedCallIds } = await client.sendCalls({ + from: await signer.getAddress(), capabilities: { paymasterService: { policyId: config.policyId, From a618441d26dfefb5ff3058a42ec145aee952fff5 Mon Sep 17 00:00:00 2001 From: jakehobbs Date: Mon, 26 Jan 2026 16:46:01 -0800 Subject: [PATCH 4/7] fix(docs): fix wallet api bash examples --- .../pages/smart-wallets/session-keys/api.mdx | 403 +++++++++--------- .../pages/transactions/swap-tokens/api.mdx | 2 +- 2 files changed, 207 insertions(+), 198 deletions(-) diff --git a/fern/wallets/pages/smart-wallets/session-keys/api.mdx b/fern/wallets/pages/smart-wallets/session-keys/api.mdx index d197ffc29..caa0e65e2 100644 --- a/fern/wallets/pages/smart-wallets/session-keys/api.mdx +++ b/fern/wallets/pages/smart-wallets/session-keys/api.mdx @@ -5,212 +5,221 @@ url: https://alchemy.com/docs/wallets/reference/wallet-apis-session-keys/api slug: wallets/reference/wallet-apis-session-keys/api --- -### 1. Request an Account for the Owner Signer - -Given an owner address, call `wallet_requestAccount` to return the smart account address for that owner. The [owner](/docs/wallets/signer/what-is-a-signer) address can be any signer (or public key) that has the ability to sign transactions. - -* If you want to use social sign up / log in, you can simply use the [SDK](/docs/wallets/react/getting-started) to authenticate users and retrieve their signer address -* If instead, you want to generate and control wallets with a custodied owner, you can generate any public private key pair (e.g. any EOA) - -This will return the account address associated with the given signer, as well as a uuid you could use to differentiate between accounts for the same signer in the future. - -```bash -curl --request POST \ - --url https://api.g.alchemy.com/v2/API_KEY \ - --header 'accept: application/json' \ - --header 'content-type: application/json' \ - --data ' -{ - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "0xOWNER_ADDRESS" - } - ] -} -' -``` - -This will return the smart account address associated with the given signer: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "accountAddress": "0xACCOUNT_ADDRESS", - "id": "af638-a8..." - } -} -``` - -### 2. Create a Session With the Session Key Signer - - - If you are using an EIP-7702 account, the account must be delegated onchain - before creating the session. If the account has already sent calls, it will - already be delegated. If it hasn't sent any calls before creating the session - key, you can delegate it by sending an empty call as the owner. - - -To create a session key using onchain policies: - -* Get the public address of a key you want to use as a session key. This can be any key pair that has the ability to sign (aka a [signer](/docs/wallets/signer/what-is-a-signer) that is either an local [signer](/wallets/reference/aa-sdk/core/classes/LocalAccountSigner) like an EOA or signer generated with a signer provider). -* Create a session for that key, by passing it as the `publicKey` in a call to `wallet_createSession`. (Note that this must be the public key **address**, not the full public key.) - -Note that the expiry is in seconds and represents a UNIX timestamp (e.g. 1776657600 for April 20th, 2077). - -```bash -curl --request POST \ - --url https://api.g.alchemy.com/v2/API_KEY \ - --header 'accept: application/json' \ - --header 'content-type: application/json' \ - --data ' -{ - "jsonrpc": "2.0", - "id": 1, - "method": "wallet_createSession", - "params": [ - { - "account": "0xACCOUNT_ADDRESS", - "chainId": "0xCHAIN_ID", - "expirySec": UNIX_TIMESTAMP_EXPIRY_IN_SECONDS, - "key": { - "publicKey": "0xSESSION_KEY_ADDRESS", - "type": "secp256k1", - }, - "permissions": [ - { - "type": "root" + + The examples provided use [jq](https://jqlang.org/) to parse json and + [foundry](https://getfoundry.sh/) to sign payloads. + + + + + EIP-7702 accounts must be delegated onchain before creating a session. If the account has already sent calls, it will already be delegated. If it hasn't sent any calls before, delegate it by sending an empty call as the owner. See [Send Transactions](/wallets/transactions/send-transactions) for the complete flow. + + ```bash + # Prepare and send a delegation transaction + PREPARE_RESPONSE=$(curl -s --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --header 'content-type: application/json' \ + --data '{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_prepareCalls", + "params": [ + { + "calls": [{"to": "0x0000000000000000000000000000000000000000", "data": "0x", "value": "0x0"}], + "from": "'$SIGNER_ADDRESS'", + "chainId": "'$CHAIN_ID'", + "capabilities": { + "paymasterService": {"policyId": "'$POLICY_ID'"} } - ] + } + ] + }') + + # Sign and send (see Send Transactions for complete signing flow) + ``` + + + + To create a session key: + + * Get the public address of a key you want to use as a session key. This can be any key pair that has the ability to sign (aka a [signer](/docs/wallets/signer/what-is-a-signer) that is either a local [signer](/wallets/reference/aa-sdk/core/classes/LocalAccountSigner) like an EOA or signer generated with a signer provider). + * Create a session for that key, by passing it as the `publicKey` in a call to `wallet_createSession`. (Note that this must be the public key **address**, not the full public key.) + + Use your signer address directly as the `account` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. + + Note that the expiry is in seconds and represents a UNIX timestamp (e.g. 1776657600 for April 20th, 2077). + + ```bash + curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --header 'content-type: application/json' \ + --data ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "wallet_createSession", + "params": [ + { + "account": "'$SIGNER_ADDRESS'", + "chainId": "'$CHAIN_ID'", + "expirySec": '$EXPIRY_TIMESTAMP', + "key": { + "publicKey": "'$SESSION_KEY_ADDRESS'", + "type": "secp256k1" + }, + "permissions": [ + { + "type": "root" + } + ] + } + ] + }' + ``` + + This will return two key elements: + + 1. The session ID + 2. The signature request that must be signed by the account owner to authorize the session key + + Keep note of the session ID, you'll need it later! + + ```json + { + "jsonrpc": "2.0", + "id": 1, + "result": { + "sessionId": "0xSESSION_ID", + "signatureRequest": { + "type": "eth_signTypedData_v4", + "data": {...}, + "rawPayload": "0xRAW_PAYLOAD_TO_SIGN" + } } - ] -}' -``` - -This will return two key elements: - -1. The session ID -2. The signature request that must be signed by the account owner to authorize the session key + } + ``` + + + + Sign the signature request using the account owner's key, then store the resulting signature. + + The `signatureRequest.type` is `eth_signTypedData_v4`, indicating this is EIP-712 typed data. You can either: + + * Sign the full typed data object using `eth_signTypedData_v4` + * Sign the `rawPayload` hash directly (without adding a message prefix) + + + When using foundry's `cast wallet sign`, use the `--no-hash` flag for the `rawPayload` since it's already an EIP-712 hash that should be signed without the Ethereum signed message prefix. + + + ```bash + # Using foundry cast + SESSION_SIGNATURE=$(cast wallet sign --no-hash --private-key "$OWNER_PRIVATE_KEY" "$RAW_PAYLOAD") + ``` + + + + With the session ID received in step 2 and the signature from step 3, we're now ready to prepare some calls! + + ```bash + curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --header 'content-type: application/json' \ + --data ' + { + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_prepareCalls", + "params": [ + { + "capabilities": { + "paymasterService": { + "policyId": "'$POLICY_ID'" + }, + "permissions": { + "sessionId": "'$SESSION_ID'", + "signature": "'$SESSION_SIGNATURE'" + } + }, + "calls": [ + { + "to": "0x0000000000000000000000000000000000000000" + } + ], + "from": "'$SIGNER_ADDRESS'", + "chainId": "'$CHAIN_ID'" + } + ] + } + ' + ``` -Keep note of the session ID, you'll need it later! + This will return the userop request (the `data` field) and a signature request, for example: -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "sessionId": "0xSESSION_ID", + ```json + { + "type": "user-operation-v070", + "data": {...useropRequest}, + "chainId": "0xCHAIN_ID", "signatureRequest": { - "type": "eth_signTypedData_v4", - "data": {...}, + "type": "personal_sign", + "data": { + "raw": "0x_HASH_TO_SIGN" + }, "rawPayload": "0xRAW_PAYLOAD_TO_SIGN" } } -} -``` - -### 3. Sign the Session Key Authorization - -Sign the signature request using the account owner's key (used in step 1), then store the resulting signature. If you are using an Alchemy Signer, you can directly sign the `rawPayload`. If you are using a library that supports signing typed data, you can use it to sign the typed data. - -### 4. Prepare Calls With the Session Key - -With the session ID received in step 2 and the signature from step 3, we're now ready to prepare some calls! - -```bash -curl --request POST \ - --url https://api.g.alchemy.com/v2/API_KEY \ - --header 'accept: application/json' \ - --header 'content-type: application/json' \ - --data ' -{ - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_prepareCalls", - "params": [ - { - "capabilities": { - "paymasterService": { - "policyId": "GAS-MANAGER-POLICY-ID", // put your gas manager policy ID here + ``` + + + + With the returned signature request, sign the userop hash using **the session key** (not the owner). This signature will be valid as long as it is within the permissions the session key has. + + Note that the `type` field in the `signatureRequest` indicates the signature type needed. In this case, we need to `personal_sign` the hash. + + ```bash + # Sign with the SESSION key (not owner!) + USEROP_SIGNATURE=$(cast wallet sign --private-key "$SESSION_PRIVATE_KEY" "$RAW_HASH") + ``` + + + + With the signature from step 5 and the `useropRequest` from step 4, you're good to go to send the call! + + ```bash + curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --header 'content-type: application/json' \ + --data ' + { + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_sendPreparedCalls", + "params": [ + { + "type": "user-operation-v070", + "data": {...useropRequest}, + "chainId": "'$CHAIN_ID'", + "capabilities": { + "permissions": { + "sessionId": "'$SESSION_ID'", + "signature": "'$SESSION_SIGNATURE'" + } }, - "permissions": { - "sessionId": "0xSESSION_ID", - "signature": "0xPERMISSION_SIG", - } - }, - "calls": [ - { - "to": "0x0000000000000000000000000000000000000000" - } - ], - "from": "0xACCOUNT_ADDRESS", - "chainId": "0xCHAIN_ID" - } - ] -} -' -``` - -This will return the userop request (the `data` field) and a signature request, for example: - -```json -{ - "type": "user-operation-v070", - "data": {...useropRequest}, - "chainId": "0xCHAIN_ID", - "signatureRequest": { - "type": "personal_sign", - "data": { - "raw": "0x_HASH_TO_SIGN", - }, - "rawPayload": "0xRAW_PAYLOAD_TO_SIGN" - } -} -``` - -### 5. Sign the userop - -With the returned signature request, all you have to do is sign the userop hash returned in the `signatureRequest` field. This should be signed with the session key. This signature will be valid as long as it is within the permissions the session key has. - -Note that the `type` field in the `signatureRequest` indicates the signature type needed. In this case, we need to `personal_sign` the hash, or, if using an Alchemy Signer, you can directly sign the `rawPayload` instead. - -### 6. Send the Prepared Calls! - -With the signature from step 5 and the `useropRequest` from step 4, you're good to go to send the call! - -```bash -curl --request POST \ - --url https://api.g.alchemy.com/v2/API_KEY \ - --header 'accept: application/json' \ - --header 'content-type: application/json' \ - --data ' -{ - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_sendPreparedCalls", - "params": [ - { - "type": "user-operation-v070", - "data": {...useropRequest}, - "chainId": "0xCHAIN_ID", // e.g. "0x66eee" for Arbitrum Sepolia - "capabilities": { - "permissions": { - "sessionId": "0xSESSION_ID", - "signature": "0xPERMISSION_SIG", + "signature": { + "type": "secp256k1", + "data": "'$USEROP_SIGNATURE'" } - }, - "signature": { - "type": "secp256k1", - "data": "0xUSEROP_SIGNATURE", } - } - ] -} -' -``` + ] + } + ' + ``` -This will return the array of prepared call IDs! + This will return the array of prepared call IDs! + + diff --git a/fern/wallets/pages/transactions/swap-tokens/api.mdx b/fern/wallets/pages/transactions/swap-tokens/api.mdx index b99f58121..fc2a75dab 100644 --- a/fern/wallets/pages/transactions/swap-tokens/api.mdx +++ b/fern/wallets/pages/transactions/swap-tokens/api.mdx @@ -27,7 +27,7 @@ curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \ "toToken": "{TO_TOKEN}", "fromAmount": "{FROM_AMOUNT_HEXADECIMAL}", "postCalls": [{ - "to:" "{POSTCALL_TO_ADDRESS}", + "to": "{POSTCALL_TO_ADDRESS}", "data": "{POSTCALL_DATA}", "value": "{POSTCALL_VALUE}" }], From 909155242614b49ecfef19027a97dff2148d04ee Mon Sep 17 00:00:00 2001 From: jakehobbs Date: Mon, 26 Jan 2026 16:58:16 -0800 Subject: [PATCH 5/7] docs: clean up wallet api docs --- .../pages/smart-wallets/quickstart/api.mdx | 155 +++++++++++------- .../cross-chain-swap-tokens/api.mdx | 2 +- .../pay-gas-with-any-token/api-postop.mdx | 2 +- .../pay-gas-with-any-token/api-preop.mdx | 2 +- .../transactions/retry-transactions/api.mdx | 2 +- .../send-batch-transactions/api.mdx | 2 +- .../send-parallel-transactions/api.mdx | 2 +- .../transactions/send-transactions/api.mdx | 2 +- .../encoding-function-data/api.mdx | 2 +- .../pages/transactions/sponsor-gas/api.mdx | 2 +- .../pages/transactions/swap-tokens/api.mdx | 2 +- .../pages/transactions/using-eip-7702/api.mdx | 2 +- .../transactions/using-eip-7702/index.mdx | 7 +- 13 files changed, 109 insertions(+), 75 deletions(-) diff --git a/fern/wallets/pages/smart-wallets/quickstart/api.mdx b/fern/wallets/pages/smart-wallets/quickstart/api.mdx index 45f35de73..e053f0bc0 100644 --- a/fern/wallets/pages/smart-wallets/quickstart/api.mdx +++ b/fern/wallets/pages/smart-wallets/quickstart/api.mdx @@ -5,59 +5,17 @@ url: https://www.alchemy.com/docs/wallets/smart-wallet-quickstart/api slug: wallets/smart-wallet-quickstart/api --- -We'll demonstrate how to use the [`wallet_requestAccount`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account), [`wallet_prepareCalls`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls), [`wallet_sendPreparedCalls`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-send-prepared-calls), and [`wallet_getCallsStatus`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-get-calls-status) endpoints. +We'll demonstrate how to use the [`wallet_prepareCalls`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls), [`wallet_sendPreparedCalls`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-send-prepared-calls), and [`wallet_getCallsStatus`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-get-calls-status) endpoints. ```mermaid flowchart LR - A[requestAccount] --> B[prepareCalls] - B --> C[sendPreparedCalls] - C --> D[getCallsStatus] + A[prepareCalls] --> B[sendPreparedCalls] + B --> C[getCallsStatus] ``` -### 1. Request an Account for the Owner Signer +### 1. Prepare Calls -Given an owner address, call `wallet_requestAccount` to return the smart account address for that owner. The [owner](/docs/wallets/signer/what-is-a-signer) address can be any signer (or public key) that has the ability to sign transactions. - -* If you want to use social sign up / log in, you can simply use the [SDK](/docs/wallets/react/getting-started) to authenticate user's and retrieve their signer address -* If instead, you want to generate and control wallets with a custodied owner, you can generate any public private key pair (e.g. any EOA) - -This will return the account address associated with the given signer, as well as a uuid you could use to differentiate between accounts for the same signer in the future. - -```bash -curl --request POST \ - --url https://api.g.alchemy.com/v2/API_KEY \ - --header 'accept: application/json' \ - --header 'content-type: application/json' \ - --data ' -{ - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_requestAccount", - "params": [ - { - "signerAddress": "0xOWNER_ADDRESS" - } - ] -} -' -``` - -This will return the smart account address associated with the given signer: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "accountAddress": "0xACCOUNT_ADDRESS", - "id": "af638-a8..." - } -} -``` - -### 2. Prepare Calls - -Now that we have an account, it's time to prepare some calls! +Use your signer address directly as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. The signer can be any Ethereum wallet key. ```bash curl --request POST \ @@ -73,7 +31,7 @@ curl --request POST \ { "capabilities": { "paymasterService": { - "policyId": GAS_MANAGER_POLICY_ID // put your gas manager policy ID here + "policyId": "GAS_MANAGER_POLICY_ID" } }, "calls": [ @@ -81,7 +39,7 @@ curl --request POST \ "to": "0x0000000000000000000000000000000000000000" } ], - "from": "0xACCOUNT_ADDRESS", + "from": "0xSIGNER_ADDRESS", "chainId": "0xCHAIN_ID" } ] @@ -89,7 +47,38 @@ curl --request POST \ ' ``` -This will return the userop request (the `data` field) and a signature request, for example: +If the account hasn't been delegated yet, the response type will be `array` containing both an EIP-7702 authorization and a user operation: + +```json +{ + "type": "array", + "data": [ + { + "type": "eip-7702-authorization", + "data": {...authorizationData}, + "chainId": "0xCHAIN_ID", + "signatureRequest": { + "type": "eth_signAuthorization", + "rawPayload": "0xAUTH_PAYLOAD_TO_SIGN" + } + }, + { + "type": "user-operation-v070", + "data": {...useropRequest}, + "chainId": "0xCHAIN_ID", + "signatureRequest": { + "type": "personal_sign", + "data": { + "raw": "0xHASH_TO_SIGN" + }, + "rawPayload": "0xRAW_PAYLOAD_TO_SIGN" + } + } + ] +} +``` + +On subsequent transactions (after the account is delegated), the response will be a single user operation: ```json { @@ -99,27 +88,71 @@ This will return the userop request (the `data` field) and a signature request, "signatureRequest": { "type": "personal_sign", "data": { - "raw": "0xHASH_TO_SIGN", + "raw": "0xHASH_TO_SIGN" }, "rawPayload": "0xRAW_PAYLOAD_TO_SIGN" } } ``` -If using Alchemy's signer service, this is the full raw payload to sign. -If using a library like Viem, you can instead use `personal_sign` to sign the hash. +### 2. Sign the Signature Request(s) -### 3. Sign the userop +Sign the returned signature request(s) using your signer key. -With the returned signature request, all you have to do is sign the signature request (returned in step 2). You'll sign this using the account owner (from step 1). +**If the account isn't delegated yet (array response):** Sign both the EIP-7702 authorization and the user operation. The authorization only needs to be signed once to delegate your account. + +**Once delegated:** Sign only the user operation. If using an Alchemy Signer, you can learn how to stamp the request on the frontend [here](/docs/reference/how-to-stamp-requests). When using the Alchemy Signer, it's easiest to sign the `signatureRequest.rawPayload` directly. -If you are using a library such as Viem that supports the `personal_sign` method, you should use that to sign the hash (since the `signatureRequest.type` is `"personal_sign"`). +If you are using a library such as Viem that supports the `personal_sign` method, you should use that to sign the user operation hash (since the `signatureRequest.type` is `"personal_sign"`). + +### 3. Send the Prepared Calls! + +With the signature(s) from step 2 and the data from step 1, you're good to send the call! + +**If the account isn't delegated yet (array response):** -### 4. Send the Prepared Calls! +```bash +curl --request POST \ + --url https://api.g.alchemy.com/v2/API_KEY \ + --header 'accept: application/json' \ + --header 'content-type: application/json' \ + --data ' +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_sendPreparedCalls", + "params": [ + { + "type": "array", + "data": [ + { + "type": "eip-7702-authorization", + "data": {...authorizationData}, + "chainId": "0xCHAIN_ID", + "signature": { + "type": "secp256k1", + "data": "0xAUTH_SIGNATURE" + } + }, + { + "type": "user-operation-v070", + "data": {...useropRequest}, + "chainId": "0xCHAIN_ID", + "signature": { + "type": "secp256k1", + "data": "0xUSEROP_SIGNATURE" + } + } + ] + } + ] +} +' +``` -With the signature from step 3 and the `useropRequest` from step 2, you're good to send the call! +**Once delegated:** ```bash curl --request POST \ @@ -135,9 +168,9 @@ curl --request POST \ { "type": "user-operation-v070", "data": {...useropRequest}, - "chainId": "0xCHAIN_ID", // E.g. "0x66eee" for Arbitrum Sepolia + "chainId": "0xCHAIN_ID", "signature": { - "type": "secp256k1" + "type": "secp256k1", "data": "0xUSEROP_SIGNATURE" } } @@ -148,7 +181,7 @@ curl --request POST \ This will return the array of prepared call IDs! -### 5. Check The Calls Status +### 4. Check The Calls Status Now you can simply call `wallet_getCallsStatus` to check the status of the calls. A “pending” state (1xx status codes) is expected for some time before the transition to “confirmed,” so this endpoint should be polled while the status is pending. If the call does not progress, refer to the [retry guide](/wallets/transactions/retry-transactions). diff --git a/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx b/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx index 65e076543..46f1512ef 100644 --- a/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx +++ b/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx @@ -76,7 +76,7 @@ This returns: Note the `callId` in the response! You'll use this to track the cross-chain swap status. Also note the `signatureRequest` - this is what you need to sign, and the returned `data` field is what you'll need to send the transaction. -On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for handling first-time authorization signing. +If the account hasn't been delegated yet, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for handling authorization signing. diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx index 1ff6f2ba1..bf78eba2d 100644 --- a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx +++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx @@ -43,6 +43,6 @@ curl --request POST \ ``` - Sign the returned signature request and send using `wallet_sendPreparedCalls`. On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See the [send transactions guide](/wallets/transactions/send-transactions) for a complete example. + Sign the returned signature request and send using `wallet_sendPreparedCalls`. If the account hasn't been delegated yet, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See the [send transactions guide](/wallets/transactions/send-transactions) for a complete example. diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx index 0d0afe2a8..3e400c6f0 100644 --- a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx +++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx @@ -74,6 +74,6 @@ - Sign and send the last prepared calls result as usual using `wallet_sendPreparedCalls`. On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See the [send transactions guide](/wallets/transactions/send-transactions) for a complete example. + Sign and send the last prepared calls result as usual using `wallet_sendPreparedCalls`. If the account hasn't been delegated yet, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See the [send transactions guide](/wallets/transactions/send-transactions) for a complete example. diff --git a/fern/wallets/pages/transactions/retry-transactions/api.mdx b/fern/wallets/pages/transactions/retry-transactions/api.mdx index 843b7f1fa..463997fa4 100644 --- a/fern/wallets/pages/transactions/retry-transactions/api.mdx +++ b/fern/wallets/pages/transactions/retry-transactions/api.mdx @@ -55,7 +55,7 @@ This returns: } ``` -Sign the `raw` field and send the transaction as usual. On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See the [sending transactions documentation](/wallets/transactions/send-transactions) for more details. +Sign the `raw` field and send the transaction as usual. If the account hasn't been delegated yet, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See the [sending transactions documentation](/wallets/transactions/send-transactions) for more details. diff --git a/fern/wallets/pages/transactions/send-batch-transactions/api.mdx b/fern/wallets/pages/transactions/send-batch-transactions/api.mdx index 38afdda2b..5e0897ad9 100644 --- a/fern/wallets/pages/transactions/send-batch-transactions/api.mdx +++ b/fern/wallets/pages/transactions/send-batch-transactions/api.mdx @@ -39,6 +39,6 @@ curl --request POST \ ``` - Sign the returned signature request and send using `wallet_sendPreparedCalls`. See [Send Transactions](/wallets/transactions/send-transactions) for a complete example including handling first-time 7702 authorization. + Sign the returned signature request and send using `wallet_sendPreparedCalls`. If the account hasn't been delegated yet, you'll also need to sign the 7702 authorization. See [Send Transactions](/wallets/transactions/send-transactions) for a complete example. diff --git a/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx b/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx index 416475259..98824a22b 100644 --- a/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx +++ b/fern/wallets/pages/transactions/send-parallel-transactions/api.mdx @@ -91,7 +91,7 @@ You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`. Note the two `signatureRequest` objects that were returned for each request! Also keep the returned `data` object from each request, you'll need them when we send the parallel transactions! - On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for handling first-time authorization signing. + If the account hasn't been delegated yet, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for handling authorization signing. diff --git a/fern/wallets/pages/transactions/send-transactions/api.mdx b/fern/wallets/pages/transactions/send-transactions/api.mdx index 616a85e5e..141ca4639 100644 --- a/fern/wallets/pages/transactions/send-transactions/api.mdx +++ b/fern/wallets/pages/transactions/send-transactions/api.mdx @@ -56,7 +56,7 @@ PREPARE_CALLS_RESPONSE=$(curl --request POST \ Sign the returned signature request. How you do this will vary depending on your environment. -On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation that need to be signed. On subsequent calls, only the user operation signature is needed. +If the account hasn't been delegated yet, the response type will be `array` containing both an EIP-7702 authorization and a user operation that need to be signed. Once delegated (after the first successful transaction), subsequent calls will only return a user operation to sign. ```bash twoslash # Extract values from prepare calls response diff --git a/fern/wallets/pages/transactions/send-transactions/encoding-function-data/api.mdx b/fern/wallets/pages/transactions/send-transactions/encoding-function-data/api.mdx index 0c378c357..ff698792f 100644 --- a/fern/wallets/pages/transactions/send-transactions/encoding-function-data/api.mdx +++ b/fern/wallets/pages/transactions/send-transactions/encoding-function-data/api.mdx @@ -1,4 +1,4 @@ -You can use [foundry](https://getfoundry.sh/cast/reference/calldata#cast-calldata) to encode function call data. +You can use [foundry](https://getfoundry.sh/cast/reference/calldata#cast-calldata) to encode function call data. Use your signer address directly as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. ```bash twoslash focus={14} curl --request POST \ diff --git a/fern/wallets/pages/transactions/sponsor-gas/api.mdx b/fern/wallets/pages/transactions/sponsor-gas/api.mdx index 639ce75cf..f3458d2bc 100644 --- a/fern/wallets/pages/transactions/sponsor-gas/api.mdx +++ b/fern/wallets/pages/transactions/sponsor-gas/api.mdx @@ -34,6 +34,6 @@ curl --request POST \ ``` - Sign the returned signature request and send using `wallet_sendPreparedCalls`. See [Send Transactions](/wallets/transactions/send-transactions) for a complete example including handling first-time 7702 authorization. + Sign the returned signature request and send using `wallet_sendPreparedCalls`. If the account hasn't been delegated yet, you'll also need to sign the 7702 authorization. See [Send Transactions](/wallets/transactions/send-transactions) for a complete example. diff --git a/fern/wallets/pages/transactions/swap-tokens/api.mdx b/fern/wallets/pages/transactions/swap-tokens/api.mdx index fc2a75dab..c8219e5fd 100644 --- a/fern/wallets/pages/transactions/swap-tokens/api.mdx +++ b/fern/wallets/pages/transactions/swap-tokens/api.mdx @@ -75,7 +75,7 @@ This returns: Note the `signatureRequest`! This is what you now have to sign, and the returned `data` field is what you will need to send the transaction. -On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for handling first-time authorization signing. +If the account hasn't been delegated yet, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for handling authorization signing. diff --git a/fern/wallets/pages/transactions/using-eip-7702/api.mdx b/fern/wallets/pages/transactions/using-eip-7702/api.mdx index cf87bdded..8854e0122 100644 --- a/fern/wallets/pages/transactions/using-eip-7702/api.mdx +++ b/fern/wallets/pages/transactions/using-eip-7702/api.mdx @@ -38,6 +38,6 @@ curl --request POST \ Sign the returned signature request(s) and send using `wallet_sendPreparedCalls`. - On the first transaction for a new account, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for a complete example including handling the authorization signature. + If the account hasn't been delegated yet, the response type will be `array` containing both an EIP-7702 authorization and a user operation. See [Send Transactions](/wallets/transactions/send-transactions) for a complete example including handling the authorization signature. diff --git a/fern/wallets/pages/transactions/using-eip-7702/index.mdx b/fern/wallets/pages/transactions/using-eip-7702/index.mdx index c9ec873f1..532efe314 100644 --- a/fern/wallets/pages/transactions/using-eip-7702/index.mdx +++ b/fern/wallets/pages/transactions/using-eip-7702/index.mdx @@ -183,9 +183,10 @@ Once delegated, the user's account behaves as a Smart Wallet while keeping the s ``` **When to use SCA mode:** - * When you need backwards compatibility with existing Smart Contract Accounts - * When working with chains that don't yet support EIP-7702 - * When you have specific requirements for smart contract account architecture + * Backwards compatibility with existing Smart Contract Accounts is required + * Using chains that don't yet support EIP-7702 + * Using a signer (EOA or embedded wallet) that doesn't support signing EIP-7702 authorizations + * You have specific requirements for smart contract account architecture ## Next steps From 600b78d4f5fe120777f903c591c12c1f2fedab2c Mon Sep 17 00:00:00 2001 From: jakehobbs Date: Mon, 26 Jan 2026 17:17:39 -0800 Subject: [PATCH 6/7] fix(docs): more wallet apis docs improvements --- fern/docs.yml | 4 +- .../pages/smart-wallets/quickstart/api.mdx | 4 + .../pages/smart-wallets/quickstart/sdk.mdx | 69 ++++----- .../cross-chain-swap-tokens/index.mdx | 6 +- .../signing/sign-messages/index.mdx | 4 + .../signing/sign-typed-data/index.mdx | 4 + .../transactions/using-eip-7702/index.mdx | 138 ++++++++---------- 7 files changed, 114 insertions(+), 115 deletions(-) diff --git a/fern/docs.yml b/fern/docs.yml index 3c568ae52..770b37137 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -1004,8 +1004,8 @@ navigation: path: wallets/pages/transactions/send-batch-transactions/index.mdx - page: Parallel transactions path: wallets/pages/transactions/send-parallel-transactions/index.mdx - - page: EIP-7702 transactions - path: wallets/pages/transactions/using-eip-7702/index.mdx + - page: How EIP-7702 works + path: wallets/pages/transactions/using-eip-7702/index.mdx - section: Sponsor gas path: wallets/pages/transactions/sponsor-gas/overview.mdx collapsed: true diff --git a/fern/wallets/pages/smart-wallets/quickstart/api.mdx b/fern/wallets/pages/smart-wallets/quickstart/api.mdx index e053f0bc0..6ffe95069 100644 --- a/fern/wallets/pages/smart-wallets/quickstart/api.mdx +++ b/fern/wallets/pages/smart-wallets/quickstart/api.mdx @@ -7,6 +7,10 @@ slug: wallets/smart-wallet-quickstart/api We'll demonstrate how to use the [`wallet_prepareCalls`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls), [`wallet_sendPreparedCalls`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-send-prepared-calls), and [`wallet_getCallsStatus`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-get-calls-status) endpoints. + + For a complete working example with bash scripts using [jq](https://jqlang.org/) and [foundry](https://getfoundry.sh/) for signing, see [Send Transactions](/wallets/transactions/send-transactions). + + ```mermaid flowchart LR A[prepareCalls] --> B[sendPreparedCalls] diff --git a/fern/wallets/pages/smart-wallets/quickstart/sdk.mdx b/fern/wallets/pages/smart-wallets/quickstart/sdk.mdx index 7fe69a7f8..7040f8856 100644 --- a/fern/wallets/pages/smart-wallets/quickstart/sdk.mdx +++ b/fern/wallets/pages/smart-wallets/quickstart/sdk.mdx @@ -46,25 +46,48 @@ const client = createSmartWalletClient({ }); ``` -### 3. Request The Account +### 3. Send A UserOp -A counterfactual address is the account address associated with the given signer-- but the account contract hasn't been deployed yet. +Use your signer address directly as the `from` field to enable [EIP-7702](/wallets/transactions/using-eip-7702) by default. This first transaction will delegate your account. ```ts -const account = await client.requestAccount(); +import { zeroAddress } from "viem"; + +const signerAddress = await signer.getAddress(); + +const preparedCalls = await client.prepareCalls({ + calls: [{ to: zeroAddress, value: "0x0" }], // callData is optional in a "data" parameter + from: signerAddress, + // "capabilities" is a data structure that hold gas manager data (as seen below) or permission data + capabilities: { + paymasterService: { + policyId: GAS_MANAGER_POLICY_ID, // put your gas manager policy ID here + }, + }, +}); -// get the address -const address = account.address; +// Sign the calls (handles both authorization and user operation if account isn't delegated yet) +const signedCalls = await client.signPreparedCalls(preparedCalls); + +// Send the userOp +const result = await client.sendPreparedCalls(signedCalls); + +// Check calls status. +const status = await client.getCallsStatus(result.preparedCallIds[0]!); ``` ### 4. Sign A Message +Once your account is delegated, you can sign messages. + ```ts import { createPublicClient } from "viem"; +const signerAddress = await signer.getAddress(); + const message = "we are so back"; -const signature = await client.signMessage({ message }); +const signature = await client.signMessage({ message, account: signerAddress }); const publicClient = createPublicClient({ chain: arbitrumSepolia, @@ -72,7 +95,7 @@ const publicClient = createPublicClient({ }); const isValid = await publicClient.verifyMessage({ - address: account.address, // fetched from await client.requestAccount() + address: signerAddress, message, signature, }); @@ -82,39 +105,11 @@ const isValid = await publicClient.verifyMessage({ ```ts // assuming you have a typedData variable -const signature = await client.signTypedData({ typedData }); +const signature = await client.signTypedData({ typedData, account: signerAddress }); const isValid = await publicClient.verifyTypedData({ - address: account.address, // fetched from await client.requestAccount() + address: signerAddress, ...typedData, signature, }); ``` - -### 6. Send A UserOp - -```ts -import { zeroAddress } from "viem"; - -const account = await client.requestAccount(); - -const preparedCalls = await client.prepareCalls({ - calls: [{ to: zeroAddress, value: "0x0" }], // callData is optional in a "data" parameter - from: account.address, - // "capabilities" is a data structure that hold gas manager data (as seen below) or permission data - capabilities: { - paymasterService: { - policyId: GAS_MANAGER_POLICY_ID, // put your gas manager policy ID here - }, - }, -}); - -// Sign the calls -const signedCalls = await client.signPreparedCalls(preparedCalls); - -// Send the userOp -const result = await client.sendPreparedCalls(signedCalls); - -// Check calls status. -const status = await client.getCallsStatus(result.preparedCallIds[0]!); -``` diff --git a/fern/wallets/pages/transactions/cross-chain-swap-tokens/index.mdx b/fern/wallets/pages/transactions/cross-chain-swap-tokens/index.mdx index 04318ecd8..8dd084c4e 100644 --- a/fern/wallets/pages/transactions/cross-chain-swap-tokens/index.mdx +++ b/fern/wallets/pages/transactions/cross-chain-swap-tokens/index.mdx @@ -33,13 +33,15 @@ When requesting a cross-chain swap quote, you can specify either a `fromAmount` ```tsx // Mode 1: Swap exact input amount { + // Swap exactly 0.01 USDC (10000 in hex, 6 decimals) fromAmount: "0x2710"; -} // Swap exactly 0.01 USDC (10000 in hex, 6 decimals) +} // Mode 2: Get minimum output amount { + // Get at least 0.0001 ETH (18 decimals). The amount you need to spend is calculated to get at least your desired ETH amount. minimumToAmount: "0x5AF3107A4000"; -} // Get at least 0.0001 ETH (18 decimals). The amount you need to spend is calculated to get at least your desired ETH amount. +} ``` ## Prerequisites diff --git a/fern/wallets/pages/transactions/signing/sign-messages/index.mdx b/fern/wallets/pages/transactions/signing/sign-messages/index.mdx index 83b4bbccb..e8d07edba 100644 --- a/fern/wallets/pages/transactions/signing/sign-messages/index.mdx +++ b/fern/wallets/pages/transactions/signing/sign-messages/index.mdx @@ -13,6 +13,10 @@ Smart Wallets will generate signatures that can be validated using [ERC-1271](ht * API key from your [dashboard](https://dashboard.alchemy.com/apps) * A Smart Wallet with an associated signer + + When using [EIP-7702](/wallets/transactions/using-eip-7702), your account must be delegated before signatures will be valid. Send at least one transaction to delegate your account first. + + ## What is message signing? Message signing allows users to: diff --git a/fern/wallets/pages/transactions/signing/sign-typed-data/index.mdx b/fern/wallets/pages/transactions/signing/sign-typed-data/index.mdx index be985277f..016bb5db6 100644 --- a/fern/wallets/pages/transactions/signing/sign-typed-data/index.mdx +++ b/fern/wallets/pages/transactions/signing/sign-typed-data/index.mdx @@ -11,6 +11,10 @@ This guide will teach you how to sign EIP-712 typed data with your Smart Wallet. * API key from your [dashboard](https://dashboard.alchemy.com/apps) * A Smart Wallet with an associated signer + + When using [EIP-7702](/wallets/transactions/using-eip-7702), your account must be delegated before signatures will be valid. Send at least one transaction to delegate your account first. + + ## What is typed data signing? EIP-712 typed data signing allows users to: diff --git a/fern/wallets/pages/transactions/using-eip-7702/index.mdx b/fern/wallets/pages/transactions/using-eip-7702/index.mdx index 532efe314..55549862d 100644 --- a/fern/wallets/pages/transactions/using-eip-7702/index.mdx +++ b/fern/wallets/pages/transactions/using-eip-7702/index.mdx @@ -18,19 +18,76 @@ EIP-7702 enables EOAs (Externally Owned Accounts) to delegate control to Smart W Once delegated, the user's account behaves as a Smart Wallet while keeping the same address and assets. Subsequent transactions only require a single user operation signature. -## Prerequisites +For implementation details, see the [Send Transactions](/wallets/transactions/send-transactions) guide. -* API key from your [dashboard](https://dashboard.alchemy.com/apps) +## How to use non-7702 mode -## Implementation +If you need to use a traditional Smart Contract Account instead of EIP-7702, you can opt out of the default 7702 behavior by calling `wallet_requestAccount` first. + +When you call `wallet_requestAccount` with a signer address, it creates a dedicated Smart Contract Account address. Using this SCA address (instead of your signer address) in subsequent API calls will bypass 7702 mode. + +**When to use SCA mode:** +* Backwards compatibility with existing Smart Contract Accounts +* Using chains that don't yet support EIP-7702 +* Using a signer that doesn't support signing EIP-7702 authorizations +* Specific requirements for smart contract account architecture - - + + ```ts + import { createSmartWalletClient } from "@account-kit/wallet-client"; + import { LocalAccountSigner } from "@aa-sdk/core"; + import { alchemy, sepolia } from "@account-kit/infra"; + + const signer = LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY!, + ); + + const client = createSmartWalletClient({ + transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY! }), + chain: sepolia, + signer, + }); + + // Request a Smart Contract Account + const account = await client.requestAccount(); + + // Use the SCA address as the `from` param to bypass 7702 mode + await client.sendCalls({ + from: account.address, + calls: [...], + }); + ``` - - + + ```bash + # First, request a Smart Contract Account + ACCOUNT_ADDRESS=$(curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data '{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_requestAccount", + "params": [{ "signerAddress": "'$SIGNER_ADDRESS'" }] + }' | jq -r '.result.accountAddress') + + # Use the SCA address (not the signer address) in subsequent calls + curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data '{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_prepareCalls", + "params": [{ + "calls": [{ "to": "0x...", "data": "0x" }], + "from": "'$ACCOUNT_ADDRESS'", + "chainId": "'$CHAIN_ID'" + }] + }' + ``` @@ -122,73 +179,6 @@ Once delegated, the user's account behaves as a Smart Wallet while keeping the s - - If you need to use a traditional Smart Contract Account instead of EIP-7702, you can opt out of the default 7702 behavior by calling `wallet_requestAccount` first. - - When you call `wallet_requestAccount` with a signer address, it creates a dedicated Smart Contract Account address. Using this SCA address in subsequent API calls will bypass 7702 mode. - - **SDK Example:** - ```ts - import { createSmartWalletClient } from "@account-kit/wallet-client"; - import { LocalAccountSigner } from "@aa-sdk/core"; - import { alchemy, sepolia } from "@account-kit/infra"; - - const signer = LocalAccountSigner.privateKeyToAccountSigner( - process.env.PRIVATE_KEY!, - ); - - const client = createSmartWalletClient({ - transport: alchemy({ apiKey: process.env.ALCHEMY_API_KEY! }), - chain: sepolia, - signer, - }); - - // Request a Smart Contract Account - const account = await client.requestAccount(); - - // Use the SCA address as the `from` param to bypass 7702 mode - await client.sendCalls({ - from: account.address, - calls: [...], - }); - ``` - - **API Example:** - ```bash - # First, request a Smart Contract Account - ACCOUNT_ADDRESS=$(curl --request POST \ - --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ - --header 'accept: application/json' \ - --data '{ - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_requestAccount", - "params": [{ "signerAddress": "'$SIGNER_ADDRESS'" }] - }' | jq -r '.result.accountAddress') - - # Use the SCA address (not the signer address) in subsequent calls - curl --request POST \ - --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ - --header 'accept: application/json' \ - --data '{ - "id": 1, - "jsonrpc": "2.0", - "method": "wallet_prepareCalls", - "params": [{ - "calls": [{ "to": "0x...", "data": "0x" }], - "from": "'$ACCOUNT_ADDRESS'", - "chainId": "'$CHAIN_ID'" - }] - }' - ``` - - **When to use SCA mode:** - * Backwards compatibility with existing Smart Contract Accounts is required - * Using chains that don't yet support EIP-7702 - * Using a signer (EOA or embedded wallet) that doesn't support signing EIP-7702 authorizations - * You have specific requirements for smart contract account architecture - - ## Next steps Build more: From 1cb4a965f85172762cda945dc01cf73910a76f27 Mon Sep 17 00:00:00 2001 From: jakehobbs Date: Tue, 27 Jan 2026 18:20:08 -0800 Subject: [PATCH 7/7] chore: remove "now" --- fern/wallets/pages/transactions/using-eip-7702/api.mdx | 2 +- fern/wallets/pages/transactions/using-eip-7702/client.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fern/wallets/pages/transactions/using-eip-7702/api.mdx b/fern/wallets/pages/transactions/using-eip-7702/api.mdx index 8854e0122..e1bf630a2 100644 --- a/fern/wallets/pages/transactions/using-eip-7702/api.mdx +++ b/fern/wallets/pages/transactions/using-eip-7702/api.mdx @@ -2,7 +2,7 @@ See the [`wallet_prepareCalls` API reference](/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls) for full descriptions of the parameters used in the following example. -EIP-7702 is now the default mode when using your signer address directly with `wallet_prepareCalls`. The API will automatically return an authorization request when delegation is needed. +EIP-7702 is the default mode when using your signer address directly with `wallet_prepareCalls`. The API will automatically return an authorization request when delegation is needed. diff --git a/fern/wallets/pages/transactions/using-eip-7702/client.mdx b/fern/wallets/pages/transactions/using-eip-7702/client.mdx index e4404ef30..e79be0cfd 100644 --- a/fern/wallets/pages/transactions/using-eip-7702/client.mdx +++ b/fern/wallets/pages/transactions/using-eip-7702/client.mdx @@ -1,6 +1,6 @@ Required SDK version: ^v4.61.0 -EIP-7702 is now the default mode for Smart Wallets. Simply create a client with your signer and use `sendCalls` - the SDK will automatically handle authorization requests to delegate the EOA to Modular Account v2 when needed. +EIP-7702 is the default mode for Smart Wallets. Simply create a client with your signer and use `sendCalls` - the SDK will automatically handle authorization requests to delegate the EOA to Modular Account v2 when needed. If the signing EOA is delegated to a different smart contract, `sendCalls`