Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions fern/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
159 changes: 98 additions & 61 deletions fern/wallets/pages/smart-wallets/quickstart/api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,21 @@ 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.

<Info>
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).
</Info>

```mermaid
flowchart LR
A[requestAccount] --> B[prepareCalls]
B --> C[sendPreparedCalls]
C --> D[getCallsStatus]
```

### 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 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"
}
]
}
'
A[prepareCalls] --> B[sendPreparedCalls]
B --> C[getCallsStatus]
```

This will return the smart account address associated with the given signer:
### 1. Prepare Calls

```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 \
Expand All @@ -73,23 +35,54 @@ curl --request POST \
{
"capabilities": {
"paymasterService": {
"policyId": GAS_MANAGER_POLICY_ID // put your gas manager policy ID here
"policyId": "GAS_MANAGER_POLICY_ID"
}
},
"calls": [
{
"to": "0x0000000000000000000000000000000000000000"
}
],
"from": "0xACCOUNT_ADDRESS",
"from": "0xSIGNER_ADDRESS",
"chainId": "0xCHAIN_ID"
}
]
}
'
```

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
{
Expand All @@ -99,27 +92,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 \
Expand All @@ -135,9 +172,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"
}
}
Expand All @@ -148,7 +185,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).

Expand Down
69 changes: 32 additions & 37 deletions fern/wallets/pages/smart-wallets/quickstart/sdk.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,33 +46,56 @@ 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,
transport: transport,
});

const isValid = await publicClient.verifyMessage({
address: account.address, // fetched from await client.requestAccount()
address: signerAddress,
message,
signature,
});
Expand All @@ -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]!);
```
Loading
Loading